Как читать большой файл построчно

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

Мой код до сих пор:

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.

Какие-либо предложения?

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

Ответ 1

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

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

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

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

Ответ 2

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

  • использование with - поддерживается с помощью python 2.5 и выше
  • использование yield, если вы действительно хотите контролировать, насколько читать

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

with - хороший и эффективный питонический способ чтения больших файлов. преимущества - 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 является итерабельным.

Ссылки

Ответ 3

Для удаления новых строк:

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') - требуется [спасибо @Dave ]
  • Python 2 в Windows - open(file_path, mode='rU') - необязательный
  • Python 3 - open(file_path, newline=None) - необязательный

Параметр newline поддерживается только в Python 3 и по умолчанию None. Параметр mode умолчанию имеет значение 'r' во всех случаях. U не рекомендуется в Python 3. В Python 2 в Windows, по-видимому, используется какой-то другой механизм для перевода \r\n в \n.

Docs:

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

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

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

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

Ответ 4

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

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

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

Ответ 5

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

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

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

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

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

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

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

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

Я использую 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

[email protected]:/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 по сравнению с программой на C. Однако его недостаточно для использования чистой программы python над программой C, по крайней мере, для целей linecounting. Как правило, этот метод может использоваться для другой обработки файлов, поэтому этот код на Python по-прежнему хорош.

Вопрос: Компиляция регулярного выражения только один раз и передача его всем работникам улучшит скорость? Ответ. Предварительная компиляция 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

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

Благодаря: проекту H2O с открытым исходным кодом, Arno и Cliff и сотрудникам H2O за их отличные программные и обучающие видеоролики, которые предоставили мне вдохновение для этого чистого высокопроизводительного параллельного байтового считывателя с питонами, как показано выше. H2O выполняет параллельное чтение файлов с использованием java, может быть вызвана программами python и R и безумно быстро, быстрее, чем что-либо на планете, при чтении больших CSV файлов.

Ответ 6

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

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

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

Сколько строк у ваших файлов и на какой машине (память и процессор) работает ваш алгоритм, и что такое допустимое время выполнения?

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

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)

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

Ответ 7

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

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

for line in text:
    if len(line) > 10:
        print line

Ответ 8

Из документации 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)

Подробнее см. здесь.

Ответ 9

Я бы настоятельно рекомендовал не использовать загрузку по умолчанию, поскольку он ужасно медленный. Вы должны посмотреть на функции 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) 

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

Ответ 10

Лучший способ чтения большого файла, строка за строкой - использовать функцию python перечислять

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