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


239

我需要逐行读取一个大文件。可以说该文件的大小超过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头,尾和向后按文本文件的行读取

两者都很好!


我该怎么做才能从尾巴上读到它?从最后一行开始逐行。
Bruno Rocha-rochacbruno 2011年

这应该是一个单独的问题
cmcginty 2011年

Answers:


310

我提供此答案是因为Keith的提示虽然简洁,但并未明确关闭文件

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

30
问题仍然是,“对于infile中的行”是否会将5GB的行加载到内存中?而且,我该如何从尾巴上阅读?
Bruno Rocha-rochacbruno 2011年

66
@rochacbruno,一次只能读取一行。读取下一行时,除非您在其他地方存储了对它的引用,否则将对前一行进行垃圾回收
John La Rooy

1
@rochacbruno,不幸的是,以相反的顺序读取行并不容易。通常,您会希望从文件末尾读取大小合适的块(例如千字节到兆字节),并分割换行符(或平台上任何以char结尾的行)
John La Rooy


1
@bawejakunal,您的意思是如果一行太长而无法立即加载到内存中?这对于文本文件来说是不寻常的。for您可以使用chunk = infile.read(chunksize)读取有限大小的块而不管它们的内容如何,而不是使用遍历行的循环。您必须自己在块内搜索换行符。
John La Rooy

60

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

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
那不是将整个文件加载到内存中吗?
Bruno Rocha-rochacbruno 2011年

17

一种古老的方法:

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

2
略述:为了安全起见,建议使用“ with”语句,在您的情况下,“ with open(filename,'rt')as fh:”
prokher 2015年

16
@prokher:是的,但是我确实称它为“老派”。
PTBNL

15

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

从文档:

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

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


尽管文档将代码段显示为“典型用途”,但在循环结束时使用该代码段不会调用close()返回的FileInput类对象的方法-因此,我将避免以这种方式使用它。在Python 3.2中,它们终于fileinput与解决此问题的上下文管理器协议兼容(但是仍然无法按照所示方式编写代码)。
martineau'7

7

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

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

尽管我喜欢这种方法,但是冒着将文本中的行分成大块的风险。我亲眼看到了这一点,这意味着如果您像以前一样在文件中搜索sstring,我会想念一些,因为它们所在的行被分成了几块。有办法解决这个问题吗?使用readlines效果不佳,因为我记错了@Ariel Cabib
edo101

6

请尝试以下方法:

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

请解释?
Nikhil VJ'Mar 31'18

3
来自Python的官方文档:link 可选的buffering参数指定文件所需的缓冲区大小:0表示未缓冲,1表示行缓冲,任何其他正值表示使用(大约)该大小(以字节为单位)的缓冲区。负缓冲意味着使用系统默认值,通常对tty设备使用行缓冲,而对于其他文件则使用完全缓冲。如果省略,则使用系统默认值
jyoti das '18

在我的情况下,我用超过〜4gb的文件和两个文件处理程序(一个读,另一个写)保存了我的一天,python挂了,现在一切正常!谢谢。
Xelt

@jyotidas虽然我喜欢这种方法,但是冒着将文本中的行分成大块的风险。我亲眼看到了这一点,这意味着如果您像以前一样在文件中搜索sstring,我会想念一些,因为它们所在的行被分成了几块。有办法解决这个问题吗?使用readlines效果不好,因为我
记错

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)

注意:由于python readline标准化行尾,因此具有将DOS行尾的文档转换\r\n为Unix行尾的文档的副作用\n。我搜索该主题的全部原因是,我需要转换一个接收到一些行尾的日志文件(因为开发人员盲目地使用了各种.NET库)。我很震惊地发现,在进行初次速度测试后,我不需要回去rstrip排队了。已经很完美了!
布鲁诺·布鲁诺斯基

2

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

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

2

这是用于加载任何大小的文本文件而不会引起内存问题的代码。 它支持千兆大小的文件

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


尽管我喜欢这种方法,但是冒着将文本中的行分成大块的风险。我亲眼看到了这一点,这意味着如果您像以前一样在文件中搜索sstring,我会想念一些,因为它们所在的行被分成了几块。有办法解决这个问题吗?使用readlines效果不好,因为我
记错

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

这看起来很有希望。是按字节还是按行加载?我担心如果是按字节的话,行会被破坏..我们如何一次加载1000行并进行处理?
Nikhil VJ

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代码中“打印数据”正上方的代码。


0

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

在没有阅读行的情况下获取文本文件中的行数

已经提供的一些答案简洁明了。我喜欢其中一些。但这实际上取决于您要对文件中的数据执行的操作。就我而言,我只是想对大文本文件尽可能快地计数行数。当然,我的代码也可以修改为做其他事情,例如任何代码。


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


-1

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

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

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

希望这可以帮助。


5
这不会读取内存中的整个文件吗?该问题明确询问如何避免这种情况,因此不会回答该问题。
费米悖论
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.