提问人:384X21 提问时间:11/4/2011 最后编辑:384X21 更新时间:12/3/2022 访问量:867598
如何逐行读取大文件?
How to read a large file - line by line?
问:
我想遍历整个文件的每一行。一种方法是读取整个文件,将其保存到列表中,然后浏览感兴趣的行。这种方法会占用大量内存,所以我正在寻找替代方案。
到目前为止,我的代码:
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 距离。
2022 年 11 月 编辑:在这个问题 8 个月后提出的一个相关问题有许多有用的答案和评论。要更深入地了解 python 逻辑,请同时阅读此相关问题 如何在 Python 中逐行读取文件?
答:
按顺序排列的两种内存高效方式(第一个是最好的) -
- 使用 - 支持 Python 2.5 及更高版本
with
- 如果您真的想控制阅读量,请使用
yield
1. 使用with
with
是读取大文件的良好而有效的 Python 方式。优点 - 1) 文件对象在退出执行块后自动关闭。2)块内部的异常处理。3)内存循环逐行遍历文件对象。在内部,它执行缓冲 IO(以优化成本高昂的 IO 操作)和内存管理。with
with
for
f
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 chunk 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)
该调用按顺序读取行,但在读取后不会将它们保留在内存中,甚至只是这样,因为在 Python 中是可迭代的。fileinput.input()
file
引用
评论
for line in open(...).readlines(): <do stuff>
readlines
fileinput
来自 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)
有关详细信息,请参阅此处
这是在 Python 中读取文件的一种可能方法:
f = open(input_file)
for line in f:
do_stuff(line)
f.close()
它不会分配完整列表。它遍历各行。
评论
with open(input_file) as f:
f.close()
读取文件的正确、完全 Python 的方式如下:
with open(...) as f:
for line in f:
# Do something with 'line'
该语句处理文件的打开和关闭,包括是否在内部块中引发异常。将文件对象视为可迭代对象,它会自动使用缓冲 I/O 和内存管理,因此您不必担心大文件。with
for line in f
f
应该有一种 - 最好只有一种 - 明显的方法来做到这一点。
评论
for line in f:
__iter__
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进行处理,或缓存一些中间结果以供重用。
评论
我强烈建议不要使用默认文件加载,因为它非常慢。您应该查看 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)
将数据加载到块中,然后对其进行矩阵运算几乎总是比逐个元素地进行数据要快得多!
要去除换行符:
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 - - 必需 [感谢 @Dave]
open(file_path, mode='rU')
- Windows 上的 Python 2 - - 可选
open(file_path, mode='rU')
- Python 3 - - 可选
open(file_path, newline=None)
该参数仅在 Python 3 中受支持,默认为 。该参数在所有情况下都默认为。在 Python 3 中已弃用。在 Windows 上的 Python 2 中,其他一些机制似乎可以转换为 .newline
None
mode
'r'
U
\r\n
\n
文档:
要保留本机行终止符,请执行以下操作:
with open(file_path, 'rb') as f:
with line_native_terminated in f:
...
二进制模式仍然可以将文件解析为带有 的行。每行都将包含文件中的任何终止符。in
感谢 @katrielalex 的回答、Python 的 open() 文档和 iPython 实验。
评论
open(file_path, 'rU')
#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
评论
关于我来自哪里的一些背景。代码片段在末尾。
如果可以的话,我更喜欢使用像 H2O 这样的开源工具来执行超高性能的并行 CSV 文件读取,但这个工具在功能集上是有限的。我最终编写了大量代码来创建数据科学管道,然后再提供给 H2O 集群以进行监督学习。
我一直在从 UCI 存储库读取 8GB HIGGS 数据集等文件,甚至 40GB CSV 文件用于数据科学目的,通过添加大量并行性与多处理库的池对象和映射函数,速度要快得多。例如,使用最近邻搜索进行聚类,以及DBSCAN和马尔可夫聚类算法需要一些并行编程技巧,以绕过一些非常具有挑战性的内存和挂钟时间问题。
我通常喜欢先使用 gnu 工具将文件逐行分解为多个部分,然后将它们全部 glob-filemask 以在 python 程序中并行查找和读取它们。我通常使用1000 +部分文件之类的东西。做这些技巧对处理速度和内存限制有很大帮助。
pandas dataframe.read_csv是单线程的,因此您可以通过运行 map() 进行并行执行来执行这些技巧来使 pandas 更快。你可以使用 htop 看到,对于普通的旧顺序 pandas dataframe.read_csv,一个内核上 100% 的 CPU 是 pd.read_csv 的实际瓶颈,而不是磁盘。
我应该补充一点,我在快速显卡总线上使用的是 SSD,而不是在 SATA6 总线上使用旋转的 HD,外加 16 个 CPU 内核。
此外,我发现在某些应用程序中效果很好的另一种技术是并行 CSV 文件读取一个大文件中的所有文件,将每个工作线程以不同的偏移量启动到文件中,而不是将一个大文件预先拆分为许多部分文件。在每个并行工作线程中使用 python 的文件 seek() 和 tell() 来同时读取大文件中不同字节偏移的起始字节和结束字节位置的大文本文件。您可以对字节执行正则表达式查找,并返回换行符的计数。这是部分总和。最后,将部分求和得到 map 函数在 worker 完成后返回时的全局和。
以下是一些使用并行字节偏移技巧的基准测试示例:
我使用 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
哦,是的,是的。并行文件读取效果很好。好吧,你去吧!
如果你们中的一些人想知道,如果使用单个工作进程时 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 文件方面比地球上任何事物都快。
评论
逐行读取大文件的最佳方法是使用 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
评论
需要经常从最后一个位置读取一个大文件?
我创建了一个脚本,用于每天多次剪切Apache access.log文件。
因此,我需要在上次执行期间解析的最后一行上设置位置游标。
为此,我使用了允许将光标存储在文件中的方法。file.seek()
file.seek()
我的代码 :
ENCODING = "utf8"
CURRENT_FILE_DIR = os.path.dirname(os.path.abspath(__file__))
# This file is used to store the last cursor position
cursor_position = os.path.join(CURRENT_FILE_DIR, "access_cursor_position.log")
# Log file with new lines
log_file_to_cut = os.path.join(CURRENT_FILE_DIR, "access.log")
cut_file = os.path.join(CURRENT_FILE_DIR, "cut_access", "cut.log")
# Set in from_line
from_position = 0
try:
with open(cursor_position, "r", encoding=ENCODING) as f:
from_position = int(f.read())
except Exception as e:
pass
# We read log_file_to_cut to put new lines in cut_file
with open(log_file_to_cut, "r", encoding=ENCODING) as f:
with open(cut_file, "w", encoding=ENCODING) as fw:
# We set cursor to the last position used (during last run of script)
f.seek(from_position)
for line in f:
fw.write("%s" % (line))
# We save the last position of cursor for next usage
with open(cursor_position, "w", encoding=ENCODING) as fw:
fw.write(str(f.tell()))
评论