Як працювати з великим об'ємом рядків

Як працювати з великим об'ємом рядків.

Моя програма працює з великими об'ємами рядків.
Я використовую TStringList, але таким чином все працює повільно
і в нього більше 64KB НЕ запишеш. Як швидко маніпулювати рядками. Наприклад WinAmpУ практично не складає труднощів сортувати або просто завантажити плейлист на 2 тисячі файлів і здійснювати пошук по ним.

Чому більше 64 Кб не завантажити.


> В нього більше 64KB НЕ запишеш.

неправда

Де написано про обмеження в 64К у TStringList?

Наприклад WinAmpУ практично не складає труднощів сортувати або просто завантажити плейлист на 2 тисячі файлів і здійснювати пошук по ним.

Де написано, що що він робить це? (Вантажить 2 тисячі імен)


> В нього більше 64KB НЕ запишеш

що, прямо так і каже, мовляв, відвали, які не запишу більше 64KB.

Reindeer Moss Eater:
сортується то весь список, але ОДИН раз після його створення - інакше ніяк

з.и. все йду _пріятно_ було поспілкуватися.

У TStringList можна запхати кілька сотень Mb без проблем і гальм, так теоретично пару гигов ;-)

очевидно, сліди чудес ведуть до стрінглісту в складі TMemo
тоді все це можна пояснити

Коли я в циклі перебираю все рядки StringList то
це виходить накладно по часу.

А щодо WinAmpА якщо комусь треба можу скинути скріншот
ось у мене MP3шек рівно 1849 це після того як я штук 500 нарізав.

А в WinAmpЕ вся ця купа рядків сортується причому і за тегами за кілька секунд. Я в слова проге тільки хвилин 5 чекаю поки прочитаю тег кожного файлу. Та хоча б вантажити просто імена файлів в StringList йде довго.

2 млн рядків в TStingList сортується багато менше ніж за 1 сек - повір.
У своїй проге ти чекаєш 5 секунд не через tstringlist, а через неточності в своєму алгоритмі.

перед заповненням списку.

- відключи автосортування
- встанови св-во Capacity рівним макс.чіслу рядків, які планується додати (якщо це значення заздалегідь відомо тобі)

після заповнення списку перед викликом методів пошуку рядків по їх значенню включи автосортування

Не знаю мені здається нічого кращого Tstringlist для роботи з великою кількістю рядків не існує. І навряд чи вдасться скоротити час пошуку написавши свій алгоритм.
Бо наскільки я розумію по (цей шматок з Classes)

function TStringList.IndexOf (const S: string): Integer;
begin
ifnot Sorted then Result: = inherited IndexOf (S) else
ifnot Find (S, Result) then Result: = -1;
end;

якщо список несортоване, то пошук ведеться простим перебором, а якщо сортовані - то бінарним пошуком. А нічого ефективнішого останнього для відсортованих списків немає.

Sorted у мене відключений.
Я говорю про сортування не вбудовану.
Я згоден, що вбудована працює ідеально швидко.
Я в проге сортую списки за своїми алгоритмами так як мені потрібно сортувати наприклад обр. порядку, взагалі не стандартним чином.

Я дкмаю, що алгоритми сортування гнилі, може хтось порадити інфу по сортувань рядків?

Так до речі у мене в циклах йде звернення до VCLкам я від цього позбувся і начебто виграв кілька секунд, але все ж довго він з ними працює.

Наприклад просто перебрати всі рядки списку і перевірити кожну на входження підрядка. Якщо 1000 рядків то чекати треба.

> DDS (04.02.04 23:30) [17]

Наприклад просто перебрати всі рядки списку і перевірити кожну на входження підрядка. Якщо 1000 рядків то чекати треба.


procedure TForm1.Button1Click (Sender: TObject);
var
i: integer;
T: DWORD;
begin
with TStringList.Create do
try
for i: = 0 to 999999 do Add ( "asdladnadmasdmna");
T: = GetTickCount;
for i: = 0 to Count - 1 do Pos ( "AasasaS", Strings [i]);
Caption: = IntToStr (GetTickCount - T)
finally
Free
end
end;

результати:
1. StringList без проблем вмістив мільйон рядків.
2. Виклик Pos для кожної з мільйона рядків - менш півсекунди.

Висновок напрошується: "Справа була не в бобіні."

А через частого звернення до візуальних компонентів в циклі могет відніматися багато часу?

> DDS (05.02.04 00:07) [19]

Якщо вони при цьому перемальовувати (наприклад, оновлюють відображаються дані) - то перерисовка буде віднімати не просто багато, а ДУЖЕ багато часу.

Якщо в циклі є інваріанти - витягніть їх назовні. Якщо є звернення до властивостей об'єктів - закешіруйте їх (наприклад, найпростіша заміна властивості Text на звичайну рядок якось скоротила час роботи однієї програми з 4-х годин до 6 хвилин).

І т. Д. Розумієте, при нормальному побудові коду тих проблем, про які Ви говорите, просто не може бути. Ну ніяк.

Коротше зрозумів я, схоже, в чому пробемма.
Є у мене така функція:

function TForm1.GetKey (str: string): integer;
var i: integer;
begin
result: = - 1;
for i: = 0 to List.Count-1 do
begin
if pos (str, List [i])> 0
then begin result: = i; break; end;
end;
end;

Вона дуже часто використовується в програмі
і майже завжди приблизно ось в такому вигляді:

for i: = 0 to List.Count-1 do
begin
r: = GetKey (List [i]);
if r> 0 then List_1.Add (List [i]); //наприклад
end;

І ніяк переробити не можна.
Коротше подвійний цикл по всіх елементах списку,
і так як списки не маленькі, то

Народ, а чи не простіше потоками (TStream) все зробити?

хм. ну по перше раджу використовувати інший алгоритм Pos, не той що в System, помоему бібліотека називається FastStrings. по-друге шукати не в TStringList, а злити в один рядок із зазначенням індексування діапазонів для кожного рядка, таким чином Pos можна буде викликати лише один раз для визначення номера рядка, в якій знайдений Str.

> DDS (05.02.04 1:15) [21]
Ти розумієш, що написав? 8-0

> For i: = 0 to List.Count-1 do
> begin
> R: = GetKey (List [i]);
> If r> 0 then List_1.Add (List [i]); //наприклад
> End;

> І ніяк переробити не можна.

r завжди> 0, тому що List [k] як мінімум дорівнює List [k] для будь-якого k
Тому весь цикл приводиться до
List_1.Assign (List);

СПасибо, спробую, напишу відповідь.

> ЮЮ
r не завжди> 0 тому, що функція GetKey повертає -1 якщо її параметр не є підрядком іншого списку. А якщо є то повертає індекс рядка. І функція теж працює в циклі по всьому списку.

> Із зазначенням індексування діапазонів для кожного рядка

А по конкретніше можна. Якщо я запишу звістку StringList в один рядок і без проблем через POS то що треба, то як мені висловити номер рядка знаючи номер знайдений через POS.

Тобто треба зробити GetKey без використання циклу. Ось ідея, може і безглузда але це мені в голову прийшло:
Беремо StringList і записуємо його як Text і записуємо його в який-небудь
RichEdit. Далі використовуючи вбудований RichEdit1.FindText шукаємо текст і потім
висловлюємо номер рядка в RichEdit з найденногою. Це і буде номер з StringList.
Я не кажу, що робити треба точно так, наприклад можна не використовувати візуальний
компонент RichEdit, а RichEdit1.FindText взяти з ComCtrls.

отримуємо:
RichEdit1.PlainText: = True;
RichEdit1.Text: = List.Text;
RichEdit1.SelStart: = RichEdit1.FindText (Str, 1, Length (RichEdit1.Text), []);
RichEdit1.SelLength: = 0;

>
Як отримати номер рядка memo, в якій знаходиться курсор?
Для цього необхідно надіслати повідомлення EM_LINEFROMCHAR.
LineNumber: = Memo1.Perform (EM_LINEFROMCHAR, -1, 0);
>

Те ж саме використовувати для RichEdit.
Сильно не сваріться на все це.

> DDS (05.02.04 14:46) [27]
а ще краще встановити MS SQL Server, він швидко шукає


> DDS (05.02.04 14:46) [27]

"Індексування діапазони" може незрозумілою звичайно висловився, але насправді все просто.

s [1]: = "abc";
s [2]: = "def";
s [3]: = "ghi";

будуємо масив індексів
indexArray: array [1..3] of integer;
заповнюємо його за наступним принципом
indexArray [1]: = 1;
for i: = 2 to 3 do indexArray [i]: = indexArray [i-1] + Length * s [i-1] +1;
діставшись до останнього індексу
SetLength (strCommon, indexArray [3] + Length (s [3]));
заповнюємо strCommon
for i: = 1 to 3 do
begin
Move (s [i] [1], strCommon [indexArray [i]], Length (s [i]));
strCommon [indexArray [i] -1]: = # 13;
end;
далі отримуємо позицію підрядка
nPos: = Pos (strSubStr, strCommon);
і бінарний пошук по indexArray (тут писати не буду, сам пиши) тобі дасть номер у вихідному масиві рядків.
faststrings шукай в інеті

Я придумав дещо: (FastString знайшов)
Вантажу список в STRING шукаю подстроку а далі перебираю символи від знайденої позиції
1) вліво поки не знайду сиволов початку рядка 0Ah
2) вправо поки не знайду сиволов перекладу рядка 0Dh
Між ними моя рядок. Фактично я висловив рядок зі списку. Отримую її індекс через IndexOf.

40000мс
Але едріть-бити старий варіант працює швидше в 5 разів.
Чому НОВИЙ варіант повільніше там адже операції прості і циклу немає.

>>>
procedure TForm1.GetKey (ind: integer);
var i: integer;
s: string;
begin
s: = Drugoi_List [ind]; // Підрядок
k: = - 1;
for i: = 0 to List.Count-1 do // Просто перебираю
begin
if pos (s, List [i])> 0 // Знайшов
then begin k: = i; break; end;
end;
end;

>>>
procedure TForm1.GetKey (ind: integer);
var i, st_ln, ps: integer;
st, bx: string;
begin
tx: = List.Text; // Завантажую весь лист в STRING
tx_ln: = length (tx); // Ну і довжину за одне
st: = Drugoi_List [ind]; // Підрядок
st_ln: = length (st); // І її довжина

Пробував так само не кожен раз вантажити звістку лист в STRING а тільки 1-ий.

ВСЕ! ВСЕ! ВСЕ! ВСЕ! ВСЕ! ВСЕ! ВСЕ! ВСЕ! ВСЕ! ВСЕ! ВСЕ! ВСЕ! ВСЕ! ВСЕ! ВСЕ!
СПАСИБІ ВАМ ЛЮДИ ДОБРІ!
Я Р І Ш И ЗАБИТИ НА ВСЕ ЦЕ І ЗАЛИШИТИ КОЛИШНІЙ ВАРІАНТ.
ПРАЦЮЄ І ЛАДНО. ВСІМ ЩЕ РАЗ ДЯКУЮ!

Дуже правильна позиція, в наступний рах коли наприклад в програмі буде постійно вискакувати AV, також забий на ВСЕ

Дуже швидкий пошук у всякому разі пошук в багато мемегабайтних файлах укладають в секунду.

Схожі статті