当前位置:  开发笔记 > 编程语言 > 正文

如何在Python中逐行读取大文件

如何解决《如何在Python中逐行读取大文件》经验,为你挑选了6个好方法。

我想迭代整个文件的每一行.一种方法是通过读取整个文件,将其保存到列表中,然后浏览感兴趣的行.这种方法使用了大量内存,所以我正在寻找替代方案.

我的代码到目前为止:

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.

有什么建议?

目的是计算成对的字符串相似性,意味着对于文件中的每一行,我想与每隔一行计算Levenshtein距离.



1> Katriel..:

读取文件的正确,完全Pythonic方式如下:

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

with语句处理打开和关闭文件,包括是否在内部块中引发异常.该for line in f会将文件对象f视为可迭代,它会自动使用缓冲I/O和内存管理,所以你不必担心大文件.

应该有一个 - 最好只有一个 - 显而易见的方法.


是的,这是python 2.6及以上版本的最佳版本
如果迭代一个对象,Python会在对象方法列表中查找一个名为`__iter__`的特殊方法,它会告诉它该怎么做.文件对象定义此特殊方法以在行上返回迭代器.(大致.)
有人可以解释一下`for line in f:`是如何工作的?我的意思是,如何迭代文件对象是可能的?
我个人更喜欢生成器和协同程序来处理数据管道.
如果一个文件是一个巨大的文本文件但有一行并且想法是处理单词,那么最好的策略是什么?

2> Srikar Appal..:

排名顺序中的两种内存有效方式(第一是最好的) -

    使用with- 从python 2.5及更高版本支持

    使用,yield如果你真的想控制多少阅读

使用 with

with是读取大文件的好而有效的pythonic方式.优点 - 1)文件对象在退出with执行块后自动关闭.2)with块内的异常处理.3)内存for循环f逐行遍历文件对象.在内部它确实缓冲IO(在昂贵的IO操作上优化)和内存管理.

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中是可迭代的.

参考

    Python声明


-1在open(...)中执行`for line(...)基本上绝不是一个好主意.readlines():`.你为什么要这样?!你刚刚失去了Python聪明的缓冲迭代器IO的所有好处,没有任何好处.
@Srikar:你可以通过在顶部放置正确的方式使你的帖子明显更好,然后提到`readlines`并解释为什么它不是一件好事(因为它将文件读入内存),然后解释`fileinput `module做了以及为什么你可能想要在其他方法上使用它,然后解释文件的分块如何使IO变得更好并给出一个分块函数的例子(但是提到Python已经为你做了这个,所以你不需要至).但是,只提供五种方法来解决一个简单的问题,其中四个在这种情况下是错误的,并不好.
@katrielalex重新审视了我的答案,发现它需要重组.我可以看到早期的答案如何引起混淆.希望这将使未来的用户清楚.
@Srikar:有一个时间和地点可以为问题提供所有可能的解决方案; 教一个初学者如何做文件输入不是吗.把正确的答案埋在一个充满错误答案的长篇帖子的底部并不是好的教学.

3> Bob Stein..:

删除换行符:

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

随着通用换行符支持所有文本文件行会显得与终止'\n',无论在文件中的终止,'\r','\n',或'\r\n'.

编辑 -指定通用换行支持:

Unix上的Python 2 open(file_path, mode='rU')- 需要[感谢@Dave ]

Windows上的Python 2 - 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.

文档:

用于Python 2的open()

用于Python 3的open()

要保留本机行终止符:

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

二进制模式仍然可以将文件解析成行in.每行将包含它在文件中的任何终止符.

感谢@katrielalex的回答,Python的open() doc和iPython实验.



4> Simon Bergot..:

这是一种在python中读取文件的可能方法:

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

它没有分配完整列表.它遍布各行.


虽然这有效,但绝对不是规范的方式.规范的方法是使用上下文包装器,例如`with open(input_file)as f:`.这样就省去了`f.close()`并确保你不会忘记关闭它.防止内存泄漏等等,在读取文件时非常重要.

5> Geoffrey And..:

关于我来自哪里的一些背景.代码片段结尾.

如果可以的话,我更喜欢使用像H2O这样的开源工具来进行超高性能并行CSV文件读取,但此工具在功能集方面受到限制.我最终编写了大量代码来创建数据科学管道,然后再投入H2O集群进行监督学习.

通过在多处理库的池对象和映射函数中添加大量并行性,我一直在读取来自UCI repo的8GB HIGGS数据集等文件,甚至用于数据科学目的的40GB CSV文件.例如,使用最近邻搜索进行聚类以及DBSCAN和Markov聚类算法需要一些并行编程技巧来绕过一些严重具有挑战性的内存和挂钟时间问题.

我通常喜欢先使用gnu工具将文件逐行分成几部分,然后将它们全部文件掩码,以便在python程序中并行查找和读取它们.我常常使用1000多个部分文件.这些技巧有助于极大地提高处理速度和内存限制.

pandas dataframe.read_csv是单线程的,因此您可以通过运行map()来执行并行执行这些技巧以使pandas更快.您可以使用htop来查看使用普通旧的顺序pandas dataframe.read_csv,只有一个核心上的100%cpu是pd.read_csv中的实际瓶颈,而不是磁盘.

我应该补充一点,我在快速视频卡总线上使用SSD,而不是SATA6总线上的旋转高清,加上16个CPU内核.

另外,我发现另一种在某些应用程序中运行良好的技术是并行CSV文件在一个巨大的文件中读取所有文件,以不同的偏移量将每个工作者启动到文件中,而不是将一个大文件预分割成许多零件文件.在每个并行工作程序中使用python的文件seek()和tell()以条带形式读取大文本文件,同时在大文件中的不同字节偏移开始字节和结束字节位置.您可以对字节执行正则表达式查找,并返回换行计数.这是一个部分总和.最后总结部分和,以便在工人完成后地图函数返回时获得全局总和.

以下是使用并行字节偏移技巧的一些示例基准:

我使用2个文件:HIGGS.csv是8 GB.它来自UCI机器学习库.all_bin .csv是40.4 GB,来自我当前的项目.我使用了2个程序:Linux附带的GNU wc程序,以及我开发的纯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 GB/s,或45 Gb/s,文件啜泣速度.我的朋友,那不是没有旋转的硬盘.那实际上是三星Pro 950 SSD.

下面是gnu wc(纯C编译程序)对同一文件进行行计数的速度基准.

很酷的是你可以看到我的纯python程序在这种情况下基本上与gnu wc编译的C程序的速度相匹配.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

结论:与C程序相比,纯python程序的速度很快.但是,使用纯python程序而不是C程序是不够的,至少对于行计数目的而言.通常该技术可用于其他文件处理,因此这个python代码仍然很好.

问题:将正则表达式编译一次并将其传递给所有工作人员会提高速度吗?答:正则表达式预编译对此应用程序没有帮助.我想原因是所有工人的流程序列化和创建的开销占主导地位.

还有一件事.并行CSV文件读取是否有帮助?磁盘是瓶颈,还是CPU?他们说,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

fastread.py python程序的关键部分:

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的def只是普通的顺序代码.我把它留了下来以防其他人想要在并行编程方面做一些练习.我免费赠送了更难的部分:经过测试和运行的并行代码,为您带来学习上的好处.

感谢:由Arno和Cliff以及H2O工作人员提供的开源H2O项目,他们提供了出色的软件和教学视频,为我提供了如上所示的纯python高性能并行字节偏移阅读器的灵感.H2O使用java进行并行文件读取,可以通过python和R程序调用,并且比读取大型CSV文件时在地球上的任何东西都快速,快速.



6> cfi..:

Katrielalex提供了打开和读取一个文件的方法。

但是,算法执行的方式将为文件的每一行读取整个文件。这意味着,如果N是文件中的行数,则读取文件和计算Levenshtein距离的总次数将为N * N。由于您担心文件的大小并且不想将其保存在内存中,因此我担心生成的二次运行时间。您的算法属于O(n ^ 2)类算法,通常可以通过专业化加以改进。

我怀疑您已经在这里知道了内存与运行时间的权衡,但是也许您想研究是否存在一种有效的方法来并行计算多个Levenshtein距离。如果是这样,在这里分享您的解决方案将很有趣。

您的文件有几行,算法必须在哪种计算机上运行(内存和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)

但是问题是如何存储距离(矩阵?),并且可以受益于准备例如outer_line进行处理,或缓存一些中间结果以供重用的优势。

推荐阅读
ar_wen2402851455
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有