#python
Появилась необходимость записывать данные в определенную строку в большом файле на
пару гигов. Конкретнее, сейчас нужно считывать строку, далее ее обрабатывать, и потом
удалять с текстового файла.
Нашел решение, состоящее в том что бы считать весь файл в переменную, потом внести
изменения в переменную и по новому всё записать, но для большого объема данных это,
очевидно, не лучший вариант.
Может быть это решить можно попроще? Если нет, то собственно сам вопрос, как записать
данные в определенную строку без перезаписи всего файла?
Ответы
Ответ 1
Файл - это просто последовательность байтов, среди которых встречаются переносы строк. Т.е. если вы работаете с файлом: строка 1 строчка 2 строка 3 ...то на самом деле на диске лежит последовательность байтов: строка 1␊строчка 2␊строка 3 Вы можете перезаписать некоторые байты, не пересчитывая/перезаписывая весь файл, как внутри строки, так и между строк, например: строка 1␊XXXXXXXXXXXтрока 3 тогда, просматривая файл по строкам, вы увидите: строка 1 XXXXXXXXXXXтрока 3 ...но никак нельзя удалить кусок из середины файла, ничего не делая с той частью, которая идет после удаляемого куска. Вы можете построить код таким образом, чтобы читать из файла строки по одной и писать их сразу же в файл на новое место, например, если удаляем строку 1: читаем "строка 1", пропускаем: ↓ позиция для чтения строка 1␊строчка 2␊строка 3 ↑ позиция для записи читаем "строчка 2": ↓ позиция для чтения строка 1␊строчка 2␊строка 3 ↑ позиция для записи Записываем "строчка 2" на новое место: ↓ позиция для чтения строчка 2␊трочка 2␊строка 3 ↑ позиция для записи Аналогично, читаем "строка 3" и пишем ее на новое место: ↓ позиция для чтения строчка 2␊строка 3␊строка 3 ↑ позиция для записи Обрезаем ненужное место в конце файла: строчка 2␊строка 3␊ Если вы хотите не удалять строку, а вместо одной строки писать другую, то надо быть внимательным в случае если новая строка длиннее старой. На этот случай примера кода у меня нет. Как видно, плоские файлы - неудачный способ хранения большого объема данных, если нужно что-то менять "посередине". Если вы сами создаете этот файл, возможно стоит рассмотреть другие форматы хранения, более эффективные для нужных вам операций.Ответ 2
Для относительно небольших файлов, fileinput может работать, чтобы удалить строки из файла, удовлетворяющие заданному условию: import fileinput import os def remove_lines(filename, condition): with fileinput.FileInput(filename, inplace=True, backup='.bak') as file: for line in file: if not condition(line): print(line, end='') # keep line os.unlink(filename + '.bak') # remove backup on success Вместо удаления строки, можно её комментировать, например, записывая # в начало строки -- это не меняет кол-во байт, поэтому не требует переписывания всего последующего содержимого. mmap работает даже для больших файлов. Для быстрой работы с большим кол-вом данных, плоский текстовый файл плохо подходит. Вместо изобретения своего фиксированного бинарного формата, следует использовать уже существующие базы данных такие как SQLite, PostgreSQL, MongoDB итд. в зависимости, что больше подходит под конкретную задачу.Ответ 3
Можно сделать свой "велосипед" с бинарным файлом, "раскладка" данных примерно такая: Сначала идут текстовые строки, разделенные нулевыми байтами Дальше таблица смещений ("указателей") всех строк от начала файла. Эта таблица позволит: Добавлять строку физически в конец блока строк, но в таблице строк вставлять ее смещение сразу в нужно место, хоть в середину. Это избавит от необходимости физически перемещать кучу строк, находящихся после вставленной строки. При вставке сдвигается относительно небольшой блок данных - только таблица строк. Не удалять строку физически, а например убирать ее смещение из общей таблицы и добавлять в таблицу удаленных строк (опционально) Для того, чтобы поменять местами две строки, достаточно будет поменять местами их смещения в таблице строк. Опциональная таблица удаленных строк. Собственно, хранит смещения удаленных строк, чтобы при необходимости делать одну общую операцию "сборки мусора" на весь файл. Возможна реализация сборки мусора и без этой таблицы. Ну и в самый конец записываем размеры таблицы строк и таблицы удаленных строк. Польза от этого: Как уже говорилось, не нужно физически сдвигать строки, чтобы вставить одну строку в середину. Перезаписать таблицу строк проще, т.к. она явно значительно меньше по размеру. Чтобы добраться до N-ной строки не читать N-1 предыдущих строк - достаточно обратиться к N-ному элементу в таблице строк. "Отложенное" удаление - можно удалить строку из таблицы строк, но сдвигать строки только когда в этом будет реальная необходимость (например, если файл разрастется раза в полтора - условно, строки занимают 4 Гб, а файл без учета таблиц строк занимает 6 Гб). Для работы со одной строкой не нужно считывать весь файл (как например при использовании json) А вообще, чтобы не изобретать велосипеды, стоит посмотреть в сторону какой-нибудь базы данных. Другой вариант, если вам нужно только удалять обработанные строки, а новые добавлять только в конец, то можно сделать вот так: мы не меняем формат хранения данных, а строим таблицу смещений всех строк, и сохраняем ее в отдельный файл. Дальше, по мере обработки файла, мы просто удаляем смещения обработанных строк из таблицы смещений. При добавлении строк в конец исходного файла мы можем также добавлять их смещения в таблицу.Ответ 4
Появилась необходимость записывать данные в определенную строку в большом файле на пару гигов. Конкретнее, сейчас нужно считывать строку, далее ее обрабатывать, и потом удалять с текстового файла. Можно взять redis для хранения соответсвий <номер строки>: <имя файла> и каждую строку хранить в одтельном файле. Либо хранить прямо в redis, если его он позволяет хранить необходимое количество данных. Для хранение соответсвий можно использвать структуру sorted set. Можно еще сделать свою версию нужного функционала для хранения соответствий с redis, с помощью словаря, который, к примеру, сериализировать в json и сбрасывать на диск при остановке программы, чтобы не тратить время на восстановление этих данных при запуске.
Комментариев нет:
Отправить комментарий