如何在python中逐行阅读大文件
我想遍历整个文件的每一行。 一种方法是读取整个文件,将其保存到列表中,然后转到感兴趣的行。 这种方法使用了大量的内存,所以我正在寻找一种替代方法。
我的代码到目前为止:
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
。
有什么build议么?
编辑:目的是计算成对的string相似性,这意味着在文件中的每一行,我想要计算每隔一行Levenshtein距离。
没有人给出正确的,完全Pythonic的方式来阅读文件。 这是以下内容:
with open(...) as f: for line in f: <do something with line>
with
语句处理打开和closures文件,包括在内部块中是否引发exception。 for line in f
的for line in f
将文件对象f
当作一个迭代对象,它会自动使用缓冲IO和内存pipe理,因此您不必担心大文件。
应该有一个 – 最好只有一个 – 明显的方法来做到这一点。
两种排列顺序的记忆效率方式(首先是最好的) –
- 使用
with
– 由python 2.5及以上版本支持 - 如果你真的想控制阅读
yield
,可以使用yield
with
with
大写的方式来读大文件。 优点 – 1)文件对象退出执行块后自动closures。 2)在with
块内部exception处理。 3)循环内存循环遍历f
文件对象。 内部它缓冲IO(优化昂贵的IO操作)和内存pipe理。
with open("x.txt") as f: for line in f: do something with data
2.使用yield
有时候可能需要更细致地控制每次迭代的读取次数。 在这种情况下使用它和产量 。 注意这个方法显式需要在最后closures文件。
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()
调用顺序地读取行,但是在读取之后不会将它们保存在内存中,甚至是简单的,因为python中的file
是可迭代的。
参考
- Python语句
剥离换行符:
with open(file_path) as f: for line_terminated in f: line = line_terminated.rstrip('\n') ...
由于通用换行符支持,所有文本文件行似乎都以'\n'
结尾,无论文件中是否包含结束符'\r'
, '\n'
或'\r\n'
。
要保留本地行结束符:
with open(file_path, 'rb') as f: with line_native_terminated in f: ...
二进制模式仍然可以将文件parsing为包含in
行。 每一行都会有文件中的任何终止符。
感谢@katrielalex的回答 ,Python的open() doc和iPython实验。
这是在python中读取文件的一种可能的方式:
f = open(input_file) for line in f: do_stuff(line) f.close()
它没有分配一个完整的列表。 它遍历线。
Katrielalex提供了打开和读取一个文件的方式。
然而,你的algorithm的方式是读取整个文件的每一行文件。 这意味着如果N是文件中行的数量,那么读取文件的总量以及计算Levenshtein距离将被执行N * N。 由于您关心的是文件大小,并不想将其保存在内存中,所以我担心产生的二次运行时间 。 你的algorithm在O(n ^ 2)类algorithm中,通常可以通过专门化来改进。
我怀疑你已经知道内存与运行时间的权衡了,但也许你会想要调查是否有一个有效的方法来并行计算多个Levenshtein距离。 如果是这样,在这里分享你的解决scheme将是有趣的。
你的文件有多less行,以及你的algorithm需要运行什么types的机器(mem和cpu power),什么是容忍的运行时间?
代码看起来像:
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)
但问题是你如何存储距离(matrix?),你可以获得准备例如outer_line进行处理,或caching一些中间结果重用的优势。
#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
从fileinput .input()的python文档:
这遍历了
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)
在这里看到更多的信息
我强烈build议不要使用默认文件加载,因为它是非常慢的。 你应该看看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)
将数据加载到数据块中,然后对其进行matrix运算,要比逐个元素执行要快得多!
前面有一些关于我来自哪里的背景。 代码片段在最后。
当我可以的时候,我更喜欢使用像H2O这样的开源工具来做超高性能的并行CSV文件读取,但是这个工具在function集中是有限的。 最后我写了很多代码来创build数据科学pipe道,然后送到H2O集群进行监督学习。
为了数据科学的目的,我一直在读取8GB的HIGGS数据集,甚至是40GB的CSV文件,通过在多处理库的池对象和映射函数中增加大量的并行性,显着加快了数据读取的速度。 例如,使用最近邻search进行聚类以及DBSCAN和Markov聚类algorithm需要一些并行编程技巧来绕过一些严重具有挑战性的内存和挂钟时间问题。
我通常喜欢首先使用gnu工具将文件逐行分解成部分,然后glob-filemask将它们全部在python程序中并行地查找和读取。 我通常使用1000+部分文件。 做这些技巧非常有助于处理速度和内存限制。
大pandasdataframe.read_csv是单线程的,所以你可以通过运行一个map()来执行这些技巧,使大pandas变得更快。 你可以用htop来看看,用普通的老式连续大pandasdataframe.read_csv,只有一个核心上的100%cpu是pd.read_csv中的实际瓶颈,而不是磁盘。
我应该添加我在快速video卡总线上使用SSD,而不是在SATA6总线上旋转的高清,外加16个CPU内核。
另外,我发现的另一种技术在某些应用程序中效果很好,它是在一个巨型文件中读取所有并行CSV文件,将每个工作人员以不同的偏移量启动到文件中,而不是将一个大文件预分割成多个部分文件。 在每个并行worker中使用python的文件seek()和tell()来同时读取大文件中的大文本文件,位于大文件的不同字节偏移量的起始字节和结束字节位置。 您可以对字节执行正则expression式findall,并返回换行符的计数。 这是一个部分的总和。 最后在工作完成后总结局部和得到地图函数返回时的全局和。
以下是使用并行字节偏移技巧的一些示例基准testing:
我使用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程序的速度非常好。 但是,在C程序中使用纯Python程序还不够好,至less是为了统计目的。 通常这个技术可以用于其他文件处理,所以这个python代码还是不错的。
问题:只编译一次正则expression式并将其传递给所有工作人员会提高速度吗? 答:正则expression式预编译在这个应用程序中没有帮助。 我想这是因为所有员工的stream程序列化和创build都是占主导地位的。
还有一件事。 并行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只是普通的顺序代码。 为了防止别人想要在并行编程方面进行一些练习,我把它留了下来。 我免费赠送更难的部分:testing和工作的并行代码,为您的学习效益。
感谢:Arno和Cliff的开放源代码H2O项目以及H2O的员工为他们提供了非常棒的软件和教学video,这为我提供了如上所示的这种纯Python高性能平行字节偏移读取器的灵感。 H2O使用Java进行并行文件读取,可以通过python和R程序进行调用,而且速度非常快,比读取大型CSV文件的任何事情都快。
阅读大文件的最佳方法是逐行使用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