| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > Delphi: Общие вопросы > Работа с большими текстовыми файлами |
| Автор: Stern87 17.2.2008, 02:30 |
| Столкнулся с проблемой работы с большими файлами и перерыв DRKB и сей форум не смог найти именно то, что меня интересует. Поэтому и создал эту тему. Есть программа, которая исполняя роль прокси-сервера, попутно индексирует адреса, и их HTTP-залоговки, которые запросил пользователь. Для индексации программа использует текстовый файл и работает с ним посредством StringList. Сначала записывается адрес, далее - заголовки. Потом второй адрес и его заголовки и т.д. Как вы уже поняли, такой список сортировать нельзя. Когда пользователь заново запрашивает адрес, который уже "проиндексирован", программа должна определить номер строки необходимого адреса в индекс-файле и выдать следующие строки (это как бы заголовки) до тех пор, когда первые символы следующей выдаваемой строки будут "http://", к примеру. Поначалу поиск необходимого адреса я делал с помощью функции IndexOf, но когда размер индекс-файла переваливает за 20Мб уже создаются сложности в виду медлительности. Частично "порог" удалось отодвинуть заменив эту функцию на Pos(CRLF+link+CRLF, indx.Text) (CRLF = #$0D+#$0A;). Но уже на 30Мб всё уже возвращается на свои места. Вопрос: существует хоть какой-то вид альтернативы StringList'у? Необходимо записывать данные в конец, но при этом иметь возможность читать файл с начала, чтобы искать необходимые строки (адреса ссылок) и читать строки далее (получать заголовки) (ReadLn???) Спасибо за понимание. |
| Автор: VICTAR 17.2.2008, 04:18 |
| Может стоит посмотреть в сторону базы данных? |
| Автор: Stern87 17.2.2008, 10:59 |
| Нет. Условие - работа с текстовыми файлами, простота доступа пользователя к этому файлу. В конце-концов это как суточный лог-файл - сутки закончатся и будет новый индекс-файл. Зачем же это всё впихать в одну базу? Необходимо, чтобы это был именно файл, а не БД. Помогите. |
| Автор: VICTAR 17.2.2008, 15:14 |
Есть "легкие" базы данных, например VolgaDB. Высокая скорость работы, вся база в одном файле и т.д. Если не устраивает, попробуй FileMapping. |
| Автор: Esperito 17.2.2008, 17:30 |
| Как насчёт TFileStream? |
| Автор: Akella 17.2.2008, 19:59 |
| а списки... TList |
| Автор: Alexeis 18.2.2008, 10:57 | ||
|
| Автор: Stern87 18.2.2008, 11:01 |
| Ага, я понял, щас объясню. Обычно файлы индексации весят от 200Мб, поэтому использовать TList, на мой взгляд, не уместно, т.к. файл будет грузиться в оперативную память, а она не резиновая. TFileStream не позволяет работать с файлом как с набором строк (как это делает TextFile) произвольной длины. Но TextFile, в свою очередь не может решить вопрос о работе с файлом как для записи строк (в конец) так и чтения всех строк (от 0 - необ. задачи). THashedStringList такой же тупой как и TStringList - он помещает мой 340Мб файл в ОЗУ, а что другим программам оставить??? К тому же это не лог-файл. Он должен так выглядеть (как лог-файл). Условие осталось прежним: это должен быть текстовый файл, и ни какая БД. |
| Автор: Alexeis 18.2.2008, 11:47 |
| Поскольку строки не меняются и не удаляются, то для их хранения можно использовать файл подкачки или просто файл отображаемый в память. Вместо AnsiString юзать PChar. Каждый раз когда нужно будет выделить новый кусок мы просто присваиваем текущий указатель и затем смещаем его на длину строки. Тогда строки будут подгружаться в память по мере обращения, но работа замедлится по сравнению если бы они были в оперативке. Освобождаем удалением файла. Не будет 2х копий. Stern87, в любом случае дешевле купить 2 гига оперативки чем платить тебе зарплату, за то что ты будешь месяц прогу писать |
| Автор: Stern87 18.2.2008, 12:20 |
| Причём здесь моя зарплата и работа как таковая?? Это - прихоть. А если индекс-файл может быть больше 2Гб - еще оперативы докупать? Можно получить такой же "инструмент" как TStringList (в частности: добавление в конец и обращение к конкретной строке) только такой, который бы НЕ загружал этот текстовый файл в ОЗУ? (Это, можно сказать, тот самый вопрос из первого поста) |
| Автор: Poseidon 18.2.2008, 12:32 | ||
Можно увидеть обоснование этого? Почему именно так и не иначе? Или дело просто в: |
| Автор: Alexeis 18.2.2008, 12:43 | ||
Так я ж описал механизм. Скопируй сорс TStringList и переопредели выделение памяти строк инициализацию и т.д. короче все что понадобиться. Но память выделяй сам используя файл отображаемый в память. |
| Автор: Данкинг 18.2.2008, 12:45 |
| Я бы базу в Access хотя бы создал и с ней работал. Вот и альтернатива. |
| Автор: Alexeis 18.2.2008, 12:45 |
Дело в цене, если игра не стоит свеч, то зачем мучаться? Да и как за день может набраться лог на 2 Гига? Поиск в ОЗУ будет самым быстрым. Если грузить с харда, то все намного замедлиться. |
| Автор: greenpc 18.2.2008, 13:45 |
| Stern87, а если попробовать так: пришем в файл фиксированную длину строки, заведомо большую чем самая длинная далее через seek бегаем по файлу |
| Автор: Poseidon 18.2.2008, 14:04 |
| Задолбешься бегать по тридцатимегабайтному текстовому файлу... |
| Автор: Stern87 18.2.2008, 15:45 | ||||||
| Сокращенно "прихоть" не получилось Можно. Программа уже n-й версии. И "структура" этих индекс-файлов не меняется уже очень долго. Люди пользуются разными версиями этой программы и они могут обменивать этими файлами, объединять их, или просто править в обыкновенном блокноте. Очень желательно, чтобы "структура" осталась такой же открытой и доступной.
Можно даже упростить главный вопрос сформулировав так: что вы будете предпринимать, если окажется, что некогда ваш механизм оперирования с текстовым файлом (посредством TStringList) будет слишком медлительным и использовать слишком много ОЗУ (увеличить которое сможет далеко не каждый ваш потенциальный пользователь), и изменить структуру того текстового файла вы не можете? |
| Автор: VICTAR 18.2.2008, 15:52 |
| Stern87, ты хочешь и рыбку съесть и вырезано цензурой. Выиграешь в скорости - потеряешь в памяти и наоборот. |
| Автор: Stern87 18.2.2008, 17:05 |
| Да я уже скорость и не прошу. Мне просто нужно, чтобы ОЗУ не так кушало, а удобство было такое же как и работа с TStringList. Или, еще проще, навигация по строкам и добавление строки в конец. Вот и всё! Не так уж и много надо. |
| Автор: lukas 18.2.2008, 17:35 |
| вместо tstringlist использовать массив строк, если сравнить: поиск по массиву будет происходить раз в 5-10 быстрее чем по tstringlist, т.к. если нужно быстрота, то объекты в твоем случае лучше отбросить, только лишь на вызов TStringList.Items[i] тратиться в 10 раз больше времени чем на тот же массив arr[i]... Так что загоняй строки в массив, и работай с ним ... естественно используй динамические массивы... |
| Автор: VICTAR 18.2.2008, 17:43 | ||
Объясни в чем заключается удобство. Проще будет думать. Добавлено через 10 минут и 46 секунд
Append? А вот это я не понял |
| Автор: Stern87 18.2.2008, 18:47 |
| Писал уже - можно легко добавить строку в конец, а также, можно обратиться к любой строчке. Он открывает текстовый файл для записи строк в конец. Читать им строки открытого файла, кажется, нельзя. |
| Автор: VICTAR 18.2.2008, 18:57 |
Да, но что мешает Reset - прочитать - закрыть, Append - записать - закрыть. Или я что-то не понимаю? |
| Автор: Akella 18.2.2008, 18:59 | ||||
грузить 200 метров текста в память??? я бы тоже БД использовал Добавлено через 7 минут и 49 секунд
акцесс тормознутый Добавлено через 9 минут и 46 секунд
firebird |
| Автор: aktuba 18.2.2008, 21:09 |
| При таких жестких условиях я бы FileStream использовал. При более мягких: |
| Автор: Akella 18.2.2008, 21:11 |
| так если использовать файлстрим, то нужно весь файл грузить в память? а как искать инфу в тексте, если текст не загрузить в память? по одной строчке? типа загрузил одну, проверил, не то, выгрузил и т.д. |
| Автор: aktuba 19.2.2008, 01:02 | ||
Скорее не целиком, а блоками... |
| Автор: mutex 19.2.2008, 03:41 |
| Stern87 Запись текстового файла (то бишь Ваша строка) состоит из двух частей: ключ поиска - это URL-адрес поста, информация записи - текст поста юзера. Длина ключа поиска и длина информационной части записи являются переменными. Правильно понимаю условие задачи? Мне кажется, что GreenPC предлагает хорошую идею: использовать Seek. Только надо немного дополнить: - надо преобразовать ключ поиска в 16-ный хэш, н-р, использовать SHA-1, который выдает 20 байтный хэш; - в памяти хранить этот хэш и Integer-смещение записи от начала текстового файла; - итого: длина элемента динамического массива равна 24 байтам. Тогда требование к памяти должно снизиться и Вы можете делать поиск хэша для ключа, введенного юзером. Можно также отсортировать массив и применить бинарный поиск, или, в крайнем случае создать двоичное дерево вместо массива. |
| Автор: aktuba 19.2.2008, 06:08 | ||
mutex,
|
| Автор: greenpc 19.2.2008, 08:43 |
| mutex, немного расширю твою идею держать один файл с логом и еще один файл индексов. например с номером строки в логе и темже хешем сторки соответствено сразу можно после открытия файла лога перейти на нужную строку Poseidon, зато не кушаю память на текущий момент средняя скорость чтения с винта 20-30 мб/с а если файл еще записан одним куском (не дефрагментирован) то скорости должно хватить ЗЫ: например Kerio держит в файле индексов абсолютное смещение от начала файла, соответственно нет фиксированной длины строки. делаем seek & readln. кстати для лога 30мб файл индесков всего 800к |
| Автор: Stern87 19.2.2008, 11:42 | ||
| Ребята! Да не лог это. Это своеобразный индекс-файл: строка HTTP-адреса (текст, рисунок - всё что только может запросить браузер), а следующие строки - HTTP-заголовки этого адреса; потом опять строка HTTP-адреса, заголовки и так по кругу. Он может и выглядит из-за этого как лог, но им он не является. Еще заметил, что когда я вызываю
Определил это на глаз с помощью sleep(7000). Может я чего-то не понимаю? Почему Pos не освободил память, которую он для себя занял? |
| Автор: Rennigth 19.2.2008, 13:27 |
Все он освободил, просто системе значит эта память пока не нужна. Как понадобиться она ее заберет. |
| Автор: ASGDeveloper 19.2.2008, 13:56 | ||
| Мдя... 1) Качаешь этот архив: http://www.torry.net/vcl/internet/html/Alcinoe_cmp_3_52_15.zip 2) Находишь там юнит alFcnString 3) Используешь в своей программе аналоги функций, Pos -> ALPos, таким образом увеличишь скорость работы и не придется бороться с форматом 4) Внимательно посмотри состав архива, может еще что-нить интересное для себя найдешь. 5) Дополнительно, для освобождения памяти попробуй почаще вызывать такую функцию:
ЗЫ Я конечно бы рекомендовал использовать базу данных. Ну раз хочется - то попробуй мой совет. В противном случае тебе здесь никто больше помочь ничем не сможет. |
| Автор: Poseidon 19.2.2008, 15:29 | ||
|
| Автор: Stern87 19.2.2008, 20:31 |
| Каждый их использует по-своему. |
| Автор: lukas 19.2.2008, 21:16 |
| возьми модуль QStrings, работа со строками, для сравнения я проверял аналогичная функция Q_PosStr работает ровно в 4 раза быстрее чем стандартная функция Pos, что уж говорить о других аналогичных функциях в этом модуле... Из всего что я перепробовал, этот модуль самый быстрый при работе со строками (например тот же AcedUtils и FastStrings работает медленнее QStrings), а вообще еще раз повторюсь, используй массив вместо tstringlist, по крайней мере поиск будет в раз десять быстрее.... |
| Автор: Stern87 19.2.2008, 21:51 | ||||
Я и так использую модуль FastCode (а также FastMove) в своих компонентах. |
| Автор: ASGDeveloper 20.2.2008, 00:30 |
И там есть такая функция? |
| Автор: Stern87 20.2.2008, 00:45 | ||
|
| Автор: WaReZMEN 20.2.2008, 01:41 |
| а де QString скачать? Нашел правда последнее изменение в 2000 году были может и не то... Но я провел эксперемен взял текстовый файл размером 50000 строк (2.6 мб) Делал я слудующие искал в каждои строке символ "=" и все что боло до него писал в БД. Попробовал 3 способа: 1. Символ искал с помощью Copy если не "=" то прибовлял к строке текущий символ (тоесть проверял каждый символ пока не встречал "=") затрачено времяни 1 минута 27 секунд. 2. Символ искал с помощью Pos затем результат делал с помощью Copy () затрачено времяни 1 минута 32 секунд. 3. Символ искал с помощью Q_PosStr затем результат делал с помощью Q_Copyleft () затрачено времяни 1 минута 48 секунд. Отсюда вывод что QString нехрена не быстрее... Либо я что то не так делаю... |
| Автор: ASGDeveloper 20.2.2008, 09:33 |
А где пункт 4 с ALPos? |
| Автор: WaReZMEN 20.2.2008, 09:58 |
| ASGDeveloper, а это где взять? |
| Автор: Stern87 20.2.2008, 11:39 |
| WaReZMEN, внимательно перечитай ветку. или хотя бы последние 15 постов. QStrings можешь скачать на http://www.torry.net - просто введи в поиск "QStrings" и всё. P.S. Q_PosStr хрена и быстрее. просто ты что-то не так делаешь. |
| Автор: lukas 20.2.2008, 17:20 | ||
| WaReZMEN, Где нибудь вставь эти 2 процедуры, и сравни что быстрее.... как говориться всю работу со строками я испытал на своей крове, и ни один из FastStrings, AcedUtils не дает больше скорости чем QString, просто нужно смотреть, какие функции там работают быстрее чем обычно, например замена Delete (Q_Delete), тоже работает там намного быстрее чем обычно...
|
| Автор: WaReZMEN 21.2.2008, 01:53 |
| Ну так я ж писал что может у меня либа не та я скачал щас с TORRY.net посмотрел эта деиствительно работает быстрее... |
| Автор: WaReZMEN 21.2.2008, 07:23 |
| Вот вы лучше раскажите как в lingvo так быстро слова фильтруются??? там же дофига слов... я вот базу сделал из 300000 слов так задержка заметная (около 1-2 сек). (База на Файрберде) |
| Автор: Poseidon 21.2.2008, 10:21 | ||
|
| Автор: Stern87 21.2.2008, 17:17 |
| Я предлагаю конкретизировать вопрос еще сильнее, чтобы не было вариантов типа БД и т.д. Задача: Есть огромный текстовый файл (он уже существует) размером 400Мб. Есть чёткий запрет "Не загружать весь файл в ОЗУ", по объективным причинам (не все же могут позволить себе закупаться памятью). Вы должны уметь записывать новые строки в конец этого файла, а также уметь читать все строки от самой первой, до последней. Какие есть предложения? Если есть возможность показывайте и код. К примеру, если вы предлагаете использовать TFileStream - покажите код, который бы показывал как бы вы реализовывали эти "умения". Спасибо!! |
| Автор: Poseidon 21.2.2008, 17:31 |
| Stern87, как же ты не поймешь... Что бы реализовать это и это в принципе нужно загрузить фесь файл в память. Есле еще второй можно как-то обскакать, загружая построчно или по блокам, то что бы записать что-то в конец, нужно этот конец найти. А что бы найти конец файла, нужно загрузить в память весь файл. А вообще, я считаю, что "текстовый файл размером 400Мб" - это немецкое порно-извращение. Не всегда такую БД увидешь. Ни о какой производительности с такими обьемами и в текстовом формате речь идти не может. Выход у тебя один - переделывать архитектуру программы в сторонну БД. В БД можно запросом выбрать что тебе надо, не загружая все в память. А так же запросом добавлять новые записи, вообще не трогая старые. Так что подумай. |
| Автор: greenpc 22.2.2008, 09:23 | ||||||||||
Poseidon,
кто Вам такое сказал ? Stern87,
Poseidon,
|
| Автор: Alexeis 22.2.2008, 10:16 | ||
Несогласен. Чтобы дописать конец файла ничего не нужно грузить в память, а чтобы иметь произвольный доступ к строкам, совсем не обязательно читать весь файл. Я вот писал специальный модуль для работы с большими XML файлами, парсинг проводиться в 2 этапа. На 1 м этапе произодиться предварительный парсинг, цель которого разобрать общую структуру и создать дерево индексов. Запоминается номер 1го и последнего символа в строке и такая запись сохраняется. Теперь для получения произвольного доступа к записи, сначала идет обращение к этому индексу, потом из файла читается фрагмент начиная с 1го символа и длиной (последний - первый + 1). Но тут я его привести не могу, так как он узкоспециализированный и написан на С++ |
| Автор: Poseidon 22.2.2008, 11:31 |
| Все это хорошо, но я не думаю что те XML были в 400МБ. Вы только вдумайтесь в это: 400МБ текста... Я вообще не представляю как с таким работать. Его хоть блокнот открывает? |
| Автор: Akella 22.2.2008, 12:23 |
| блокнот такой файл не откроет, он с трудом открывает файлы размеров в несколько мегабайт |
| Автор: Alexeis 22.2.2008, 12:46 | ||
Все зависит от того как работать. Если использовать файлы отображаемые в память вместо файловых потоков и поиск с конца (чтобы искать сначала последних записях), то мож и будет работать. А если говорить вообще о полном проходе по такому файлу с целью поиска строки в нем, то конечно ничего хорошего за адекватное время не выйдет |
| Автор: Poseidon 22.2.2008, 14:31 | ||
Я имел ввиду вот это:
|
| Автор: mutex 22.2.2008, 15:05 | ||
Замечательная идея. При 400-ти мегах может выручить только индекс-технология баз данных. Как говорит автор - Stern87, файл - суточный, временный и не редактируемый, кроме дозаписи в его конец. Значит такой парсинг надо делать на лету, при созданий строк файла, начиная с ноля часов. Если уж надо минимизировать память, эту структуру надо сбрасывать в отдельный файл, который и будет индексом текстовой БД. Stern87, на Вашем месте, я бы выставил кусок файла и привел пример ключа поиска и ответа на поиск. Кстати, как это за 24 часа удается набрать такой объем данных? Что за болтливый сайт, если не секрет? |