У меня есть очень большой файл, который я хочу открыть и прочитать из него определенные строки, я всегда знаю, с каким номером строки находятся нужные мне данные, но я не хочу каждый раз читать весь файл только для того, чтобы прочитать это конкретная линия.
Есть ли способ, которым вы можете читать только определенные строки в Python? Или как это сделать наиболее эффективным способом (т.е. прочитать как можно меньше файла, чтобы ускорить выполнение)?
Если все строки имеют ТОЧНО одинаковое количество символов/байтов, то может быть способ найти эту позицию, но если строки могут быть разной длины, то невозможно узнать, где начинается вторая строка, пока вы не прочитаете строку one и нашел новую строку в конце.
Второй ответ здесь хороший подход. Однако вам придется просмотреть файл хотя бы один раз.
линейный кеш: docs.python.org/3/library/linecache.html
@Selcuk спасибо за ссылку, я этого раньше не видел. Тем не менее, похоже, что это немного другой вопрос/ответ - поскольку ответы там сосредоточены на чтении определенных строк с эффективным использованием памяти, но все же кажется, что они каждый раз читают весь файл, просто не сохраняя все строки в памяти.
@KetZoomer, спасибо, это хорошая находка, но я не думаю, что линейный кеш - хорошая идея, поскольку я понимаю, что он сначала загружает весь файл в память?
@KillerKode да, это правильно
К сожалению, это невозможно по простой причине: линий не существует. То, что ваш текстовый редактор показывает вам в виде строки, — это всего лишь два фрагмента текста с символом новой строки посередине (вы можете ввести его с помощью \n
в python. Если все строки имеют одинаковую длину, то это возможно, но я предполагаю, что это не так) дело здесь.
Наименьшее количество чтения делается, если вы читаете только свой контент + ваш контент. Это означает, что вы не должны использовать read
или readlines
. Вместо этого используйте readline
, чтобы получить и отбросить ненужные строки, а затем используйте его еще раз, чтобы получить то, что вы хотите. Это, пожалуй, самый действенный способ.
Вот несколько вариантов:
def get_lines(..., linenums: list):
with open(...) as f:
for lno, ln in enumerate(f):
if lno in linenums:
yield ln
Для файла размером 4 ГБ это заняло ~ 6 секунд для linenums = [n // 4, n // 2, n - 1]
, где n = lines_in_file
.
Мне нравится идея пункта номер 1, я могу просмотреть файл и сохранить метаданные, чтобы сказать мне, сколько искать для достижения линии, я могу принять это решение, поскольку я думаю, что это, скорее всего, лучший подход, но подожду немного дольше для большего количества ответов на всякий случай.
Только что провел некоторое тестирование, чтобы прочитать мой образец набора данных так, как я сейчас делаю в этом большом файле JSON, требуется 159 секунд только для чтения определенных частей этого файла. Когда я переформатирую те же данные в формат стиля CSV, а затем сохраняю метаданные об их смещениях в другом файле (используя подход 1), мне требуется 0,88 секунды, чтобы прочитать оба файла и получить нужные мне строки. Это более чем в 159 раз быстрее. !!! Очень хорошо, удалите пункт 2 из своего ответа, и я соглашусь.
Я также подсчитал, что даже после еще 20 лет сбора данных для загрузки нужных мне битов потребуется не более 3 секунд, что более чем достаточно для моих нужд. Сочетание отказа от JSON и поиска файла имеет огромное значение.
@KillerKode Вау, это большая разница; хороший! Я думаю, что оставлю пункт 2, потому что, хотя это не лучшее решение для вас, если кто-то еще столкнется с этим вопросом и у него будет файл среднего или меньшего размера, который будет изменен, это, вероятно, будет лучшей альтернативой.
@KillerKode хорошо, после дальнейшего изучения похоже, что linecache
в целом просто не лучшее решение. Например, он не может читать двоичные файлы, и его основная цель использования находится в самом исходном коде Python. Я обновлю свой ответ.
Отвечает ли это на ваш вопрос? Чтение только определенных строк