Читайте большие текстовые файлы в Python построчно, не загружая их в память
Мне нужно прочитать большой файл, строка за строкой. Допустим, файл имеет более 5 ГБ, и мне нужно прочитать каждую строку, но, очевидно, я не хочу использовать readlines()
потому что это создаст очень большой список в памяти.
Как будет работать код ниже для этого случая? Является xreadlines
сам читал по одному в память? Нужно ли выражение генератора?
f = (line for line in open("log.txt").xreadlines()) # how much is loaded in memory?
f.next()
Плюс, что я могу сделать, чтобы прочитать это в обратном порядке, так же как Linux tail
команда?
Я нашел:
http://code.google.com/p/pytailer/
а также
" голова питона, хвост и назад читаются по строкам текстового файла"
Оба работали очень хорошо!
16 ответов
Я предоставил этот ответ, потому что Кит, хотя и лаконичен, явно не закрывает файл
with open("log.txt") as infile:
for line in infile:
do_something_with(line)
Все, что вам нужно сделать, это использовать объект файла в качестве итератора.
for line in open("log.txt"):
do_something_with(line)
Еще лучше использовать контекстный менеджер в последних версиях Python.
with open("log.txt") as fileobject:
for line in fileobject:
do_something_with(line)
Это также автоматически закроет файл.
Пожалуйста, попробуйте это:
with open('filename','r',buffering=100000) as f:
for line in f:
print line
Вместо этого вам лучше использовать итератор. Соответствующий: http://docs.python.org/library/fileinput.html
Из документов:
import fileinput
for line in fileinput.input("filename"):
process(line)
Это позволит избежать копирования всего файла в память сразу.
Старый школьный подход:
fh = open(file_name, 'rt')
line = fh.readline()
while line:
# do stuff with line
line = fh.readline()
fh.close()
Вот что вы делаете, если в файле нет новых строк:
with open('large_text.txt') as f:
while True:
c = f.read(1024)
if not c:
break
print(c)
Я не мог поверить, что это может быть так просто, как кажется, ответ @john-la-rooy. Итак, я воссоздал cp
команда, использующая построчное чтение и запись. Это безумно быстро.
#!/usr/bin/env python3.6
import sys
with open(sys.argv[2], 'w') as outfile:
with open(sys.argv[1]) as infile:
for line in infile:
outfile.write(line)
Этот блестящий проект прошел долгий путь за последние 6 лет. Он имеет простой API, охватывающий полезный набор функций панд.
dask.dataframe обеспечивает внутреннее распределение, поддерживает множество параллельных операций и позволяет легко экспортировать фрагменты обратно в панды для операций в памяти.
import dask.dataframe as dd
df = dd.read_csv('filename.csv')
df.head(10) # return first 10 rows
df.tail(10) # return last 10 rows
# iterate rows
for idx, row in df.iterrows():
...
# group by my_field and return mean
df.groupby(df.my_field).value.mean().compute()
# slice by column
df[df.my_field=='XYZ'].compute()
Вот код для загрузки текстовых файлов любого размера без проблем с памятью.Он поддерживает файлы размером в гигабайты
https://gist.github.com/iyvinjose/e6c1cb2821abd5f01fd1b9065cbc759d
скачайте файл data_loading_utils.py и импортируйте его в свой код
использование
import data_loading_utils.py.py
file_name = 'file_name.ext'
CHUNK_SIZE = 1000000
def process_lines(data, eof, file_name):
# check if end of file reached
if not eof:
# process data, data is one single line of the file
else:
# end of file reached
data_loading_utils.read_lines_from_file_as_data_chunks(file_name, chunk_size=CHUNK_SIZE, callback=self.process_lines)
Методprocess_lines является функцией обратного вызова. Он будет вызываться для всех строк, причем данные параметров представляют одну строку файла за раз.
Вы можете настроить переменную CHUNK_SIZE в зависимости от конфигурации оборудования вашего компьютера.
Я понимаю, что на это был дан ответ довольно давно, но вот способ сделать это параллельно, не убивая накладные расходы на память (что было бы в случае, если бы вы попытались запустить каждую строку в пул). Очевидно, замените функцию readJSON_line2 на что-нибудь разумное - это просто для иллюстрации сути!
Ускорение будет зависеть от размера файла и того, что вы делаете с каждой строкой, но в худшем случае для небольшого файла и просто чтения его с помощью средства чтения JSON я вижу производительность, аналогичную ST с настройками ниже.
Надеюсь, будет полезно кому-то там:
def readJSON_line2(linesIn):
#Function for reading a chunk of json lines
'''
Note, this function is nonsensical. A user would never use the approach suggested
for reading in a JSON file,
its role is to evaluate the MT approach for full line by line processing to both
increase speed and reduce memory overhead
'''
import json
linesRtn = []
for lineIn in linesIn:
if lineIn.strip() != 0:
lineRtn = json.loads(lineIn)
else:
lineRtn = ""
linesRtn.append(lineRtn)
return linesRtn
# -------------------------------------------------------------------
if __name__ == "__main__":
import multiprocessing as mp
path1 = "C:\\user\\Documents\\"
file1 = "someBigJson.json"
nBuffer = 20*nCPUs # How many chunks are queued up (so cpus aren't waiting on processes spawning)
nChunk = 1000 # How many lines are in each chunk
#Both of the above will require balancing speed against memory overhead
iJob = 0 #Tracker for SMP jobs submitted into pool
iiJob = 0 #Tracker for SMP jobs extracted back out of pool
jobs = [] #SMP job holder
MTres3 = [] #Final result holder
chunk = []
iBuffer = 0 # Buffer line count
with open(path1+file1) as f:
for line in f:
#Send to the chunk
if len(chunk) < nChunk:
chunk.append(line)
else:
#Chunk full
#Don't forget to add the current line to chunk
chunk.append(line)
#Then add the chunk to the buffer (submit to SMP pool)
jobs.append(pool.apply_async(readJSON_line2, args=(chunk,)))
iJob +=1
iBuffer +=1
#Clear the chunk for the next batch of entries
chunk = []
#Buffer is full, any more chunks submitted would cause undue memory overhead
#(Partially) empty the buffer
if iBuffer >= nBuffer:
temp1 = jobs[iiJob].get()
for rtnLine1 in temp1:
MTres3.append(rtnLine1)
iBuffer -=1
iiJob+=1
#Submit the last chunk if it exists (as it would not have been submitted to SMP buffer)
if chunk:
jobs.append(pool.apply_async(readJSON_line2, args=(chunk,)))
iJob +=1
iBuffer +=1
#And gather up the last of the buffer, including the final chunk
while iiJob < iJob:
temp1 = jobs[iiJob].get()
for rtnLine1 in temp1:
MTres3.append(rtnLine1)
iiJob+=1
#Cleanup
del chunk, jobs, temp1
pool.close()
Спасибо! Я недавно перешел на Python 3 и был разочарован использованием readlines(0) для чтения больших файлов. Это решило проблему. Но чтобы получить каждую строчку, мне пришлось сделать пару дополнительных шагов. Каждой строке предшествовала буква "b", которая, я думаю, была в двоичном формате. Использование "decode(utf-8)" изменило его ascii.
Затем мне пришлось удалить "=\n" в середине каждой строки.
Затем я разбил строки на новой строке.
b_data=(fh.read(ele[1]))#endat This is one chunk of ascii data in binary format
a_data=((binascii.b2a_qp(b_data)).decode('utf-8')) #Data chunk in 'split' ascii format
data_chunk = (a_data.replace('=\n','').strip()) #Splitting characters removed
data_list = data_chunk.split('\n') #List containing lines in chunk
#print(data_list,'\n')
#time.sleep(1)
for j in range(len(data_list)): #iterate through data_list to get each item
i += 1
line_of_data = data_list[j]
print(line_of_data)
Вот код, начинающийся чуть выше "данных печати" в коде Арохи.
Как насчет этого? Разделите ваш файл на куски, а затем прочитайте его построчно, потому что когда вы читаете файл, ваша операционная система будет кэшировать следующую строку. Если вы читаете файл построчно, вы неэффективно используете кэшированную информацию.
Вместо этого разделите файл на куски и загрузите весь кусок в память, а затем выполните обработку.
def chunks(file,size=1024):
while 1:
startat=fh.tell()
print startat #file's object current position from the start
fh.seek(size,1) #offset from current postion -->1
data=fh.readline()
yield startat,fh.tell()-startat #doesnt store whole list in memory
if not data:
break
if os.path.isfile(fname):
try:
fh=open(fname,'rb')
except IOError as e: #file --> permission denied
print "I/O error({0}): {1}".format(e.errno, e.strerror)
except Exception as e1: #handle other exceptions such as attribute errors
print "Unexpected error: {0}".format(e1)
for ele in chunks(fh):
fh.seek(ele[0])#startat
data=fh.read(ele[1])#endat
print data
В этом другом вопросе я продемонстрировал подход произвольного доступа на параллельном уровне байтов:
Получение количества строк в текстовом файле без readlines
Некоторые из уже предоставленных ответов хороши и лаконичны. Мне нравятся некоторые из них. Но это действительно зависит от того, что вы хотите сделать с данными в файле. В моем случае я просто хотел посчитать строки как можно быстрее для больших текстовых файлов. Мой код может быть изменен, чтобы делать другие вещи, конечно, как любой код.
Лучшее решение, которое я нашел по этому поводу, и я попробовал его на файле размером 330 МБ.
lineno = 500
line_length = 8
with open('catfour.txt', 'r') as file:
file.seek(lineno * (line_length + 2))
print(file.readline(), end='')
Где line_length - количество символов в одной строке. Например, "abcd" имеет длину строки 4.
Я добавил 2 строки в длину, чтобы пропустить символ '\n' и перейти к следующему символу.
Это может быть полезно, если вы хотите работать параллельно и читать только фрагменты данных, но сохранять их чистыми с помощью новых строк.
def readInChunks(fileObj, chunkSize=1024):
while True:
data = fileObj.read(chunkSize)
if not data:
break
while data[-1:] != '\n':
data+=fileObj.read(1)
yield data
f=open('filename','r').read()
f1=f.split('\n')
for i in range (len(f1)):
do_something_with(f1[i])
надеюсь это поможет.