Как бороться с очень большим текстовым файлом? - PullRequest
13 голосов
/ 18 января 2011

Я сейчас пишу что-то, что должно обрабатывать очень большие текстовые файлы (по крайней мере, несколько ГиБ). Здесь нужно (и это исправлено):

  • На основе CSV, в соответствии с RFC 4180, за исключением встроенных разрывов строк
  • произвольный доступ для чтения к строкам, хотя в основном построчно и ближе к концу
  • добавление строк в конце
  • (смена строк). Очевидно, что это требует перезаписи остальной части файла, это тоже редко, поэтому в данный момент не особо важно

Размер файла запрещает хранить его полностью в памяти (что также нежелательно, поскольку при добавлении изменения должны сохраняться как можно скорее).

Я думал об использовании отображенной в памяти области в качестве окна в файл, который перемещается, если запрашивается строка вне его диапазона. Конечно, на этом этапе у меня все еще нет абстракции выше уровня байтов. Чтобы реально работать с содержимым, у меня есть CharsetDecoder, дающий мне CharBuffer. Теперь проблема в том, что я могу справиться со строками текста, вероятно, просто отлично в CharBuffer, но мне также нужно знать смещение байтов этой строки в файле (чтобы сохранить кэш строковых индексов и смещений, чтобы я не Чтобы найти конкретную строку, снова нужно отсканировать файл с самого начала.

Есть ли способ сопоставить смещения в CharBuffer со смещениями в ByteBuffer? Это очевидно тривиально с ASCII или ISO-8859- *, менее с UTF-8 и с ISO 2022 или BOCU-1 все станет совершенно безобразно (не то, чтобы я действительно ожидал, что последние два, но UTF-8 должен быть здесь по умолчанию здесь - и до сих пор создает проблемы).

Полагаю, я мог бы просто преобразовать часть CharBuffer в байты снова и использовать длину. Либо это работает, либо у меня возникают проблемы с диакритическими знаками, и в этом случае я, вероятно, могу поручить использование NFC или NFD, чтобы гарантировать, что текст всегда однозначно закодирован.

Тем не менее, мне интересно, если это даже путь сюда. Есть ли лучшие варианты?

ETA: Некоторые ответы на общие вопросы и предложения здесь:

Это хранилище данных для прогонов симуляции, предназначенное для локальной альтернативы полноценной базе данных. У нас есть и базы данных, и они используются, но для случаев, когда они недоступны или неприменимы, мы этого хотим.

Я также поддерживаю только подмножество CSV (без встроенных разрывов строк), но пока это нормально. Проблемные моменты здесь в значительной степени заключаются в том, что я не могу предсказать, насколько длинные линии, и, следовательно, необходимо создать приблизительную карту файла.

Что касается того, что я обрисовал в общих чертах выше: я размышлял о том, что могу легко определить конец строки на уровне символов (U + 000D + U + 000A), но я не хотел предполагать, что это выглядит как 0A 0D на уровне байтов (который уже не срабатывает для UTF-16, например, где это либо 0D 00 0A 00, либо 00 0D 00 0A). Я думал, что смогу изменить кодировку символов, не используя жесткие детали кодировки, которую я сейчас использую. Но я думаю, что я мог бы просто придерживаться UTF-8 и делать все остальное. Хотя как-то нехорошо.

Ответы [ 7 ]

2 голосов
/ 18 января 2011

Очень трудно поддерживать отображение 1: 1 между последовательностью символов Java (которые по сути являются UTF-16) и байтами, которые могут быть любыми в зависимости от кодировки вашего файла.Даже с UTF-8 «очевидное» отображение 1 байта на 1 символ работает только для ASCII.Ни UTF-16, ни UTF-8 не гарантируют, что символ Unicode может быть сохранен на одном компьютере char или byte.

Я бы поддерживал мое окно в файле как байтовый буфер, а не символбуфер.Затем, чтобы найти окончания строк в байтовом буфере, я бы закодировал строку Java "\r\n" (или, возможно, просто "\n") как последовательность байтов, используя ту же кодировку, что и файл. Затем я использовал бы эту последовательность байтовискать окончания строк в байтовом буфере.Положение конца строки в буфере + смещение буфера от начала файла отображается точно в байтовую позицию в файле окончания строки.

Добавление строк - это всего лишь случай поискаконец файла и добавление новых строк.Смена линий сложнее.Я думаю, что я бы сохранил список или карту положений байтов измененных строк и того, что это за изменение.Когда все готово для записи изменений:

  1. сортировка списка изменений по позиции байта
  2. чтение исходного файла до следующего изменения и запись его во временный файл.
  3. записать измененную строку во временный файл.
  4. пропустить измененную строку в исходном файле.
  5. вернуться к шагу 2, если вы не достигли конца исходного файла
  6. переместить временный файл поверх исходного файла.
1 голос
/ 18 января 2011

Можно ли разделить файл на «подфайлы» (конечно, вы не должны разбивать его на один символ Utf-8)? Затем вам понадобятся метаданные для каждого из подфайлов (общее количество символов и общее количество строк).

Если у вас это есть, а «подфайлы» относительно малы, так что вы всегда можете загрузить один из них полностью, тогда обработка становится легкой.

Даже редактирование становится легким, потому что вам нужно только обновить «подфайл» и его метаданные.

Если бы вы подошли к краю: тогда вы могли бы использовать базу данных и хранить по одной строке на строку базы данных. - Если это хорошая идея, сильно зависит от вашего варианта использования .

0 голосов
/ 18 января 2011

Как насчет таблицы смещений через несколько равных интервалов в файле, чтобы вы могли возобновить синтаксический анализ где-то рядом с искомым местом?

Идея состоит в том, что это будет смещение байтов, когда кодированиебудет в исходном состоянии (т. е. если данные были закодированы в стандарте ISO-2022, то это место будет в режиме, совместимом с ASCII).Любой индекс в данных будет состоять из указателя на эту таблицу плюс все, что требуется для поиска фактической строки.Если вы поместите точки перезапуска так, чтобы каждая из них находилась между двумя точками, вписывается в окно mmap, то вы можете пропустить код проверки / переотображения / перезапуска из слоя синтаксического анализа и использовать синтаксический анализатор, который предполагает, что данные последовательно отображаются.

0 голосов
/ 18 января 2011

В случае фиксированного числа столбцов я бы разбил файл логически и / или физически на столбцы и реализовал некоторые оболочки / адаптеры для задач ввода-вывода и управления файлом в целом.

0 голосов
/ 18 января 2011
  • Нахождение начала строки:

Придерживайтесь UTF-8 и \ n, обозначающих конец строки, не должно быть проблемой. В качестве альтернативы вы можете разрешить UTF-16 и распознать данные: они должны быть заключены в кавычки (например), иметь N запятых (точки с запятой) и другой конец строки. Можно прочитать заголовок, чтобы узнать, сколько столбцов в структуре.

  • Вставка в середину файла

может быть достигнуто путем резервирования некоторого пространства в конце / начале каждой строки.

  • добавление строк в конце

Это тривиально, пока файл заблокирован (как и любые другие модификации)

0 голосов
/ 18 января 2011

Если у вас были линии фиксированной ширины, то использование RandomAccessFile может решить многие ваши проблемы.Я понимаю, что ваши строки, вероятно, не фиксированной ширины, но вы могли бы искусственно навязать это, добавив индикатор конца строки и затем добавив строки (например, с пробелами).

Это, очевидно, работает лучше всегоесли ваш файл в настоящее время имеет довольно равномерное распределение длин строк и не имеет очень длинных строк.Недостатком является то, что это искусственно увеличит размер вашего файла.

0 голосов
/ 18 января 2011

CharBuffer предполагает, что все символы UTF-16 или UCS-2 (возможно, кто-то знает разницу)символ или где n-я строка.Я использую текстовые файлы объемом несколько ГБ, но предполагаю наличие данных ASCII-7, и я читаю / пишу только последовательно.

Если вам нужен произвольный доступ к неиндексированному текстовому файлу, вы не можете ожидать его высокой производительности.

Если вы хотите купить новый сервер, вы можете получить его с 24 ГБоколо 1800 фунтов стерлингов и 64 ГБ за 4200 фунтов стерлингов.Это позволит вам загружать даже файлы размером в несколько ГБ в память.

...