Как читать большой файл построчно - PullRequest
501 голосов
/ 04 ноября 2011

Я хочу перебрать каждую строку всего файла. Один из способов сделать это - прочитать весь файл, сохранить его в списке, а затем перейти по интересующей линии. Этот метод использует много памяти, поэтому я ищу альтернативу.

Пока мой код:

for each_line in fileinput.input(input_file):
    do_something(each_line)

    for each_line_again in fileinput.input(input_file):
        do_something(each_line_again)

При выполнении этого кода выдается сообщение об ошибке: device active.

Есть предложения?

Цель состоит в том, чтобы рассчитать парное сходство строк, то есть для каждой строки в файле я хочу вычислить расстояние Левенштейна для каждой другой строки.

Ответы [ 10 ]

1218 голосов
/ 04 ноября 2011

Правильный, полностью Pythonic способ чтения файла следующий:

with open(...) as f:
    for line in f:
        # Do something with 'line'

Оператор with обрабатывает открытие и закрытие файла, в том числе, если во внутреннем блоке возникает исключение. for line in f рассматривает файловый объект f как итеративный, который автоматически использует буферизованный ввод-вывод и управление памятью, поэтому вам не нужно беспокоиться о больших файлах.

Должен быть один - и желательно только один - очевидный способ сделать это.

119 голосов
/ 04 ноября 2011

Два способа экономии памяти в ранжированном порядке (первый - лучший) -

  1. использование with - поддерживается с python 2.5 и выше
  2. использование yield, если выочень хочу иметь контроль над тем, сколько читать

1.использование with

with - хороший и эффективный способ чтения больших файлов с помощью pythonic.Преимущества - 1) файловый объект автоматически закрывается после выхода из with исполнительного блока.2) обработка исключений внутри блока with.3) цикл памяти for построчно перебирает объект файла f.внутренне он выполняет буферизованный ввод-вывод (для оптимизации дорогостоящих операций ввода-вывода) и управление памятью.

with open("x.txt") as f:
    for line in f:
        do something with data

2.использование yield

Иногда может потребоваться более детальный контроль над тем, сколько читать в каждой итерации.В этом случае используйте iter & yield .Обратите внимание, что при использовании этого метода необходимо явно закрыть файл в конце.

def readInChunks(fileObj, chunkSize=2048):
    """
    Lazy function to read a file piece by piece.
    Default chunk size: 2kB.
    """
    while True:
        data = fileObj.read(chunkSize)
        if not data:
            break
        yield data

f = open('bigFile')
for chuck in readInChunks(f):
    do_something(chunk)
f.close()

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

В Python наиболее распространенный способ чтения строк из файла заключается в следующем:

for line in open('myfile','r').readlines():
    do_something(line)

Когда это сделанооднако функция readlines() (то же самое относится и к функции read()) загружает весь файл в память, а затем перебирает его.Немного лучший подход (первые два упомянутых метода являются лучшими) для больших файлов заключается в использовании модуля fileinput следующим образом:

import fileinput

for line in fileinput.input(['myfile']):
    do_something(line)

вызов fileinput.input() читает строки последовательно, но не 'хранить их в памяти после того, как они были прочитаны или даже просто так, поскольку file в python является итеративным.

Ссылки

  1. Python с утверждением
36 голосов
/ 15 сентября 2015

Чтобы удалить символы новой строки:

with open(file_path, 'rU') as f:
    for line_terminated in f:
        line = line_terminated.rstrip('\n')
        ...

При универсальная поддержка новой строки все строки текстового файла будут заканчиваться '\n', независимо от того, какие терминаторы в файле '\r', '\n' или '\r\n'.

РЕДАКТИРОВАТЬ - Чтобы указать универсальную поддержку новой строки:

  • Python 2 в Unix - open(file_path, mode='rU') - требуется [спасибо @ Дейв ]
  • Python 2 в Windows - open(file_path, mode='rU') - необязательно
  • Python 3 - open(file_path, newline=None) - необязательно

Параметр newlineподдерживается только в Python 3 и по умолчанию None.Параметр mode по умолчанию равен 'r' во всех случаях.* * * * * * * * * * * * * * * * * * * * * * * * * * * * U * * * * * * * * * * * * * * * * * * * * *1035* * * * * * * * * * * * * * * * * * *1035* * * * * * * * * * * * * * * * * *1035* * * * * * * * * * * * * * * * * * *1035*

1043 open.() для Python 2

open () для Python 3

Для сохранения собственных ограничителей строки:

with open(file_path, 'rb') as f:
    with line_native_terminated in f:
        ...

Двоичный режим все еще может анализировать файл на строки с in.Каждая строка будет иметь те терминаторы, которые есть в файле.

Благодаря @ katrielalex ' answer , Python's open () doc и iPython эксперименты.

17 голосов
/ 04 ноября 2011

это возможный способ чтения файла в python:

f = open(input_file)
for line in f:
    do_stuff(line)
f.close()

не выделяет полный список. Он перебирает строки.

10 голосов
/ 02 февраля 2017

Какой-то контекст в отношении того, откуда я иду.Фрагменты кода в конце.

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

Я читал файлы, такие как набор данных HIGGS 8 ГБ из репозитория UCI и даже файлы CSV 40 ГБ для науки о данныхзначительно ускоряет выполнение задач, добавляя много параллелизма к объекту пула многопроцессорной библиотеки и функции карты.Например, кластеризация с поиском ближайшего соседа, а также алгоритмами кластеризации DBSCAN и Маркова требует некоторого изощренного параллельного программирования, чтобы обойти некоторые серьезные проблемы с памятью и временем настенных часов.

Мне обычно нравится разбивать файл по строкам на части, используя сначала инструменты gnu, а затем glob-filemask их все, чтобы найти и прочитать их параллельно в программе python.Я обычно использую что-то вроде 1000+ частичных файлов.Выполнение этих приемов очень помогает в скорости обработки и ограничении памяти.

pandas dataframe.read_csv является однопоточным, поэтому вы можете выполнить эти приемы, чтобы сделать панды более быстрыми, запустив map () для параллельного выполнения.Вы можете использовать htop, чтобы увидеть, что с обычными старыми последовательными пандами dataframe.read_csv 100% ЦП только на одном ядре является фактическим узким местом в pd.read_csv, а не на диске вообще.

Я должен добавить, что яиспользование SSD на быстрой шине видеокарты, а не вращающийся HD на шине SATA6, плюс 16 ядер процессора.

Кроме того, я обнаружил, что другой метод отлично работает в некоторых приложениях - это параллельное чтение файла CSV в одном гигантском файле.запуск каждого работника с разным смещением в файле, а не предварительное разбиение одного большого файла на несколько файлов деталей.Используйте Python для поиска файлов () и Tell () в каждом параллельном работнике, чтобы читать большой текстовый файл в виде полос, с разными байтовыми смещениями начальных и конечных байтов в большом файле, одновременно в одно и то же время.Вы можете выполнить поиск по всем регулярным выражениям в байтах и ​​вернуть счетчик перевода строки.Это частичная сумма.Наконец, суммируйте частичные суммы, чтобы получить глобальную сумму, когда функция карты возвращается после того, как рабочие закончили.

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

Я использую 2 файла:HIGGS.csv составляет 8 ГБ.Это из хранилища машинного обучения UCI.all_bin .csv имеет размер 40,4 ГБ и является результатом моего текущего проекта.Я использую 2 программы: GNU wc, которая поставляется с Linux, и программу на python fastread.py, которую я разработал.

HP-Z820:/mnt/fastssd/fast_file_reader$ ls -l /mnt/fastssd/nzv/HIGGS.csv
-rw-rw-r-- 1 8035497980 Jan 24 16:00 /mnt/fastssd/nzv/HIGGS.csv

HP-Z820:/mnt/fastssd$ ls -l all_bin.csv
-rw-rw-r-- 1 40412077758 Feb  2 09:00 all_bin.csv

ga@ga-HP-Z820:/mnt/fastssd$ time python fastread.py --fileName="all_bin.csv" --numProcesses=32 --balanceFactor=2
2367496

real    0m8.920s
user    1m30.056s
sys 2m38.744s

In [1]: 40412077758. / 8.92
Out[1]: 4530501990.807175

Это примерно 4,5 ГБ / с, или 45 Гбит / с, скорость потери файла,Это не вращающийся жесткий диск, мой друг.Это на самом деле Samsung Pro 950 SSD.

Ниже приведен тест скорости для того же файла, который подсчитывается строкой gnu wc, скомпилированной программой на чистом языке C.

Что круто, вы можете видеть, что моя программа на чистом Python практически соответствует скоростискомпилированного gnu wc программы в этом случае.Python интерпретируется, но C компилируется, так что это довольно интересный подвиг скорости, я думаю, вы согласитесь.Конечно, wc действительно нужно заменить на параллельную программу, и тогда он действительно побьет все мои программы на python.Но в нынешнем виде gnu wc - это просто последовательная программа.Вы делаете то, что можете, и Python может делать параллельное сегодня.Компиляция Cython может помочь мне (в другое время).Кроме того, отображенные в память файлы еще не исследовались.

HP-Z820:/mnt/fastssd$ time wc -l all_bin.csv
2367496 all_bin.csv

real    0m8.807s
user    0m1.168s
sys 0m7.636s


HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=16 --balanceFactor=2
11000000

real    0m2.257s
user    0m12.088s
sys 0m20.512s

HP-Z820:/mnt/fastssd/fast_file_reader$ time wc -l HIGGS.csv
11000000 HIGGS.csv

real    0m1.820s
user    0m0.364s
sys 0m1.456s

Вывод: Скорость хороша для чистой программы на Python по сравнению с программой на Си.Тем не менее, недостаточно использовать чистую программу на Python поверх программы на C, по крайней мере, для целей строкового подсчета.Обычно эту технику можно использовать для обработки других файлов, поэтому этот код на Python все еще хорош.

QuСуть: компиляция регулярного выражения только один раз и передача его всем работникам улучшит скорость? Ответ: Предварительная компиляция Regex НЕ помогает в этом приложении. Я полагаю, причина в том, что накладные расходы на сериализацию и создание процессов для всех работников являются доминирующими.

Еще одна вещь. Параллельное чтение файла CSV даже помогает? Является ли диск узким местом или процессором? Многие так называемые ответы с самым высоким рейтингом в stackoverflow содержат общую мудрость разработчика, согласно которой вам нужен только один поток, чтобы прочитать файл, говорят они, лучше всего. Хотя они уверены?

Давайте выясним:

HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=16 --balanceFactor=2
11000000

real    0m2.256s
user    0m10.696s
sys 0m19.952s

HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=1 --balanceFactor=1
11000000

real    0m17.380s
user    0m11.124s
sys 0m6.272s

О да, да, это так. Параллельное чтение файлов работает довольно хорошо. Ну вот, пожалуйста!

Ps. В случае, если некоторые из вас хотят знать, что если balanceFactor равнялся 2 при использовании одного рабочего процесса? Ну, это ужасно:

HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=1 --balanceFactor=2
11000000

real    1m37.077s
user    0m12.432s
sys 1m24.700s

Ключевые части программы python fastread.py:

fileBytes = stat(fileName).st_size  # Read quickly from OS how many bytes are in a text file
startByte, endByte = PartitionDataToWorkers(workers=numProcesses, items=fileBytes, balanceFactor=balanceFactor)
p = Pool(numProcesses)
partialSum = p.starmap(ReadFileSegment, zip(startByte, endByte, repeat(fileName))) # startByte is already a list. fileName is made into a same-length list of duplicates values.
globalSum = sum(partialSum)
print(globalSum)


def ReadFileSegment(startByte, endByte, fileName, searchChar='\n'):  # counts number of searchChar appearing in the byte range
    with open(fileName, 'r') as f:
        f.seek(startByte-1)  # seek is initially at byte 0 and then moves forward the specified amount, so seek(5) points at the 6th byte.
        bytes = f.read(endByte - startByte + 1)
        cnt = len(re.findall(searchChar, bytes)) # findall with implicit compiling runs just as fast here as re.compile once + re.finditer many times.
    return cnt

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

Спасибо: проект H2O с открытым исходным кодом, разработанный Арно и Клиффом и сотрудниками H2O за их отличное программное обеспечение и обучающие видео, которые вдохновили меня на этот высокопроизводительный параллельный считыватель байтовых смещений на python, как показано выше. H2O выполняет параллельное чтение файлов с использованием Java, вызывается программами на Python и R и быстро работает с умом, быстрее, чем что-либо на планете, при чтении больших файлов CSV.

5 голосов
/ 04 ноября 2011

Katrielalex предоставил способ открыть и прочитать один файл.

Однако, как работает ваш алгоритм, он читает весь файл для каждой строки файла.Это означает, что общий объем чтения файла - и вычисление расстояния Левенштейна - будет выполнено N * N, если N - количество строк в файле.Поскольку вас беспокоит размер файла и вы не хотите хранить его в памяти, меня беспокоит результирующее квадратичное время выполнения .Ваш алгоритм относится к классу алгоритмов O (n ^ 2), которые часто могут быть улучшены с помощью специализации.

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

Сколько строк содержит ваши файлы и на каком компьютере (мощность mem & cpu) должен работать ваш алгоритм, и какова допустимая среда выполнения?

Код будет выглядеть так:

with f_outer as open(input_file, 'r'):
    for line_outer in f_outer:
        with f_inner as open(input_file, 'r'):
            for line_inner in f_inner:
                compute_distance(line_outer, line_inner)

Но вопрос в том, как вы сохраняете расстояния (матрицу?) И можете ли вы получить преимущество от подготовки, например, external_line для обработки, иликэширование некоторых промежуточных результатов для повторного использования.

3 голосов
/ 30 июля 2016
#Using a text file for the example
with open("yourFile.txt","r") as f:
    text = f.readlines()
for line in text:
    print line
  • Открыть файл для чтения (r)
  • Читать весь файл и сохранять каждую строку в списке (текст)
  • Loopчерез список печатая каждую строку.

Если вы хотите, например, проверить конкретную строку на длину больше 10, поработайте с тем, что у вас уже есть.

for line in text:
    if len(line) > 10:
        print line
2 голосов
/ 17 октября 2014

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

http://docs.scipy.org/doc/numpy/user/basics.io.genfromtxt.html

https://store.continuum.io/cshop/iopro/

Тогда вы можете разбить попарнооперация в чанках:

import numpy as np
import math

lines_total = n    
similarity = np.zeros(n,n)
lines_per_chunk = m
n_chunks = math.ceil(float(n)/m)
for i in xrange(n_chunks):
    for j in xrange(n_chunks):
        chunk_i = (function of your choice to read lines i*lines_per_chunk to (i+1)*lines_per_chunk)
        chunk_j = (function of your choice to read lines j*lines_per_chunk to (j+1)*lines_per_chunk)
        similarity[i*lines_per_chunk:(i+1)*lines_per_chunk,
                   j*lines_per_chunk:(j+1)*lines_per_chunk] = fast_operation(chunk_i, chunk_j) 

Почти всегда гораздо быстрее загружать данные в чанки и затем выполнять над ними матричные операции, чем делать их поэлементно !!

2 голосов
/ 04 ноября 2011

Из документации Python для fileinput .input ():

Это перебирает строки всех файлов, перечисленных в sys.argv[1:], по умолчанию sys.stdin, если список пуст

далее, определение функции:

fileinput.FileInput([files[, inplace[, backup[, mode[, openhook]]]]])

чтение между строк означает, что files может быть списком, поэтому вы можете получить что-то вроде:

for each_line in fileinput.input([input_file, input_file]):
  do_something(each_line)

См. здесь для получения дополнительной информации

0 голосов
/ 24 августа 2017

Лучший способ прочитать большой файл, строка за строкой - использовать python перечислить function

with open(file_name, "rU") as read_file:
    for i, row in enumerate(read_file, 1):
        #do something
        #i in line of that line
        #row containts all data of that line
...