如何逐行读取Python中的大文本文件,而不将其加载到内存中?

时间:2011-06-25 02:04:15

标签: python

我需要逐行读取一个大文件。假设文件超过5GB,我需要读取每一行,但显然我不想使用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/

python head, tail and backward read by lines of a text file

两者都运作良好!

16 个答案:

答案 0 :(得分:259)

我提供了这个答案,因为Keith虽然简洁,却没有明确关闭文件

with open("log.txt") as infile:
    for line in infile:
        do_something_with(line)

答案 1 :(得分:50)

您需要做的就是使用文件对象作为迭代器。

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)

这也会自动关闭文件。

答案 2 :(得分:16)

旧学校方法:

fh = open(file_name, 'rt')
line = fh.readline()
while line:
    # do stuff with line
    line = fh.readline()
fh.close()

答案 3 :(得分:13)

最好使用迭代器。相关:http://docs.python.org/library/fileinput.html

来自文档:

import fileinput
for line in fileinput.input("filename"):
    process(line)

这样可以避免一次将整个文件复制到内存中。

答案 4 :(得分:3)

我无法相信它可以像@ 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)

答案 5 :(得分:3)

如果您在文件中没有换行符,则执行以下操作:

with open('large_text.txt') as f:
  while True:
    c = f.read(1024)
    if not c:
      break
    print(c)

答案 6 :(得分:2)

请试试这个:

with open('filename','r',buffering=100000) as f:
    for line in f:
        print line

答案 7 :(得分:1)

blaze项目在过去的6年中取得了长足的进步。它有一个简单的API,涵盖了一个有用的pandas功能子集。

dask.dataframe负责内部分块,支持许多可并行操作,并允许您将切片轻松导出回到pandas以进行内存操作。

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()

答案 8 :(得分:0)

这个怎么样? 将文件分成块然后逐行读取,因为当您读取文件时,操作系统将缓存下一行。如果您逐行读取文件,则无法有效使用缓存的信息。

相反,将文件分成块并将整个块加载到内存中然后进行处理。

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

答案 9 :(得分:0)

谢谢!我最近转换为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)

以下是Arohi代码中“打印数据”上方的代码。

答案 10 :(得分:0)

我在另一个问题中展示了并行字节级随机访问方法:

Getting number of lines in a text file without readlines

已经提供的一些答案简洁明了。我喜欢其中的一些。但这实际上取决于你想要对文件中的数据做什么。在我的情况下,我只想在大文本文件上尽可能快地计算行数。当然,我的代码也可以修改为做其他事情,就像任何代码一样。

答案 11 :(得分:0)

此处提供了用于加载任何大小的文本文件而不会引起内存问题的代码。 它支持千兆字节的文件

https://gist.github.com/iyvinjose/e6c1cb2821abd5f01fd1b9065cbc759d

下载文件 data_loading_utils.py 并将其导入您的代码

用法

Glide.with(this).load("your image uri").asBitmap().toBytes().fitCenter().into(new SimpleTarget<byte[]>() {
      @Override
      public void onResourceReady(byte[] resource, GlideAnimation<? super byte[]> glideAnimation) {
          imageEditorView.resetImage(BitmapFactory.decodeByteArray(resource, 0, resource.length));
      }
});

process_lines 方法是回调函数。所有行都会调用它,参数数据一次代表文件的一行。

您可以根据计算机的硬件配置来配置变量 CHUNK_SIZE

答案 12 :(得分:0)

当您要并行工作并仅读取大块数据但用新行保持干净时,这可能很有用。

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

答案 13 :(得分:0)

我找到了与此相关的最佳解决方案,并在330 MB文件上进行了尝试。

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'字符并移至下一个字符。

答案 14 :(得分:0)

我意识到这已经在很久以前得到了回答,但这里有一种并行执行的方法,而不会杀死您的内存开销(如果您试图将每一行都发送到池中,就会出现这种情况)。显然,将 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()

答案 15 :(得分:-9)

f=open('filename','r').read()
f1=f.split('\n')
for i in range (len(f1)):
    do_something_with(f1[i])

希望这会有所帮助。