总算发现python如何读取大文件

核心提示:在处理大数据时,有可能会碰到好几个G大小的文件。如果通过一些工具(例如:NotePad++)打开它,会发生错误,无法读取任何内容。

在处理大数据时,有可能会碰到好几个 G 大小的文件。如果通过一些工具(例如:NotePad++)打开它,会发生错误,无法读取任何内容。那么python如何读取大文件呢?一起来了解下吧:

python如何读取大文件

python读取文件一般情况是利用open()函数以及read()函数来完成:

f = open(filename,'r')
f.read()

这种方法读取小文件,即读取远远大小小于内存的文件显然没有什么问题。但是如果是将一个10G大小的日志文件读取,即文件大于内存的大小,这么处理就有问题了,会造成MemoryError ... 也就是发生内存溢出。

发生这种错误的原因在于,read()方法执行操作是一次性的都读入内存中,显然文件大于内存就会报错。

解决方法:

这里发现跟read()类似的还有其他的方法:read(参数)、readline()、readlines()

(1)read(参数):通过参数指定每次读取的大小长度,这样就避免了因为文件太大读取出问题。

while True:
block = f.read(1024)
if not block:
break

(2)readline():每次读取一行

while True:
line = f.readline()
if not line:
break

(3)readlines():读取全部的行,构成一个list,通过list来对文件进行处理,但是这种方式依然会造成MemoyError

for line in f.readlines():
....

以上基本分析了python中读取文件的方法,但是总感觉不能达到python中所强调的优雅,后来发现了还有下面的解决方法:

pythonic(我理解的是很python的python代码)的解决办法:

with open(filename, 'r') as flie:
for line in file:
....

对可迭代对象file进行迭代,这样会自动的使用buffered IO以及内存管理,这样就不必担心大文件问题了。

后来,又发现了一个模块:linecache,这个模块也可以解决大文件读取的问题,并且可以指定读取哪一行,

# 输出第2行
text = linecache.getline(filename, 2)

Python怎么读取大文件

一般的读取

读取文件,最常见的方式是:

with open('filename', 'r', encoding = 'utf-8') as f:
for line in f.readlines():
do_something(line)

但是,当完成这一操作时,readlines() 方法(read() 也一样)会将整个文件加载到内存中。在文件较大时,往往会引发 MemoryError(内存溢出)。

那么,如何避免这个问题?

使用 fileinput 模块

稍微好点儿的方式是使用 fileinput 模块:

import fileinput
for line in fileinput.input(['filename']):
do_something(line)

调用 fileinput.input() 会按照顺序读取行,但是在读取之后不会将它们保留在内存中。

逐行读取

除此之外,也可使用 while() 循环和 readline() 来逐行读取:

with open('filename', 'r', encoding = 'utf-8') as f:
while True:
line = f.readline()  # 逐行读取
if not line:  # 到 EOF,返回空字符串,则终止循环
break
do_something(line)

指定每次读取的长度

有时,可能希望对每次读取的内容进行更细粒度的控制。

在这种情况下,可以使用 iter 和 yield:

def read_in_chunks(file_obj, chunk_size = 2048):
"""

逐件读取文件

默认块大小:2KB

"""
while True:
data = file_obj.read(chunk_size)  # 每次读取指定的长度
if not data:
break
yield data
with open('filename', 'r', encoding = 'utf-8') as f:
for chuck in read_in_chunks(f):
do_something(chunk)

自动管理

这才是 Pythonci 最完美的方式,既高效又快速:

with open('filename', 'r', encoding = 'utf-8') as f:
for line in f:
do_something(line)

with 语句句柄负责打开和关闭文件(包括在内部块中引发异常时),for line in f 将文件对象 f 视为一个可迭代的数据类型,会自动使用 IO 缓存和内存管理,这样就不必担心大文件了。

Python读取大文件的方法

准备工作

我们谈到“文本处理”时,我们通常是指处理的内容。Python 将文本文件的内容读入可以操作的字符串变量非常容易。文件对象提供了三个“读”方法: .read()、.readline() 和 .readlines()。每种方法可以接受一个变量以限制每次读取的数据量,但它们通常不使用变量。

.read() 每次读取整个文件,它通常用于将文件内容放到一个字符串变量中。然而.read() 生成文件内容最直接的字符串表示,但对于连续的面向行的处理,它却是不必要的,并且如果文件大于可用内存,则不可能实现这种处理。下面是read()方法示例:

try:
f = open('/path/to/file', 'r')
print f.read()
finally:
if f:
f.close()

调用read()会一次性读取文件的全部内容,如果文件有10G,内存就爆了,所以,要保险起见,可以反复调用read(size)方法,每次最多读取size个字节的内容。另外,调用readline()可以每次读取一行内容,调用readlines()一次读取所有内容并按行返回list。因此,要根据需要决定怎么调用。

如果文件很小,read()一次性读取最方便;如果不能确定文件大小,反复调用read(size)比较保险;如果是配置文件,调用readlines()最方便:

for line in f.readlines():
process(line) #

分块读取

处理大文件是很容易想到的就是将大文件分割成若干小文件处理,处理完每个小文件后释放该部分内存。这里用了iter 和 yield:

def read_in_chunks(filePath, chunk_size=1024*1024):
"""
Lazy function (generator) to read a file piece by piece.
Default chunk size: 1M
You can set your own chunk size
"""
file_object = open(filePath)
while True:
chunk_data = file_object.read(chunk_size)
if not chunk_data:
break
yield chunk_data
if __name__ == "__main__":
filePath = './path/filename'
for chunk in read_in_chunks(filePath):
process(chunk) # 使用With open()

with语句打开和关闭文件,包括抛出一个内部块异常。for line in f文件对象f视为一个迭代器,会自动的采用缓冲IO和内存管理,所以你不必担心大文件。

代码如下:

#If the file is line based
with open(...) as f:
for line in f:
process(line) # 优化

面对百万行的大型数据使用with open 是没有问题的,但是这里面参数的不同也会导致不同的效率。经过测试发先参数为"rb"时的效率是"r"的6倍。由此可知二进制读取依然是最快的模式。

with open(filename,"rb") as f:
for fLine in f:
pass

测试结果:rb方式最快,100w行全遍历2.9秒。基本能满足中大型文件处理效率需求。如果从rb(二级制读取)读取改为r(读取模式),慢5-6倍。

在使用python进行大文件读取时,应该让系统来处理,使用最简单的方式,交给解释器,就管好自己的工作就行了。同时根据不同的需求可以选择不同的读取参数进一步获得更高的性能。

Python大文件如何读取

1. read() 接口的问题

f = open(filename, 'rb')
f.read()

我们来读取 1 个 nginx 的日至文件,规模为 3Gb 大小。read() 方法执行的操作,是一次性全部读入内存,显然会造成:

MemoryError
...

也即会发生内存溢出。

2. 解决方案:转换接口

(1)readlines() :读取全部的行,构成一个 list,实践表明还是会造成内存的问题;

for line in f.reanlines():
...

(2)readline():每次读取一行,

while True:
line = f.readline()
if not line:
break

(3)read(1024):重载,指定每次读取的长度

while True:
block = f.read(1024)
if not block:
break

3. 真正 Pythonic 的方法

真正 Pythonci 的方法,使用 with 结构:

with open(filename, 'rb') as f:
for line in f:

对可迭代对象 f,进行迭代遍历:for line in f,会自动地使用缓冲IO(buffered IO)以及内存管理,而不必担心任何大文件的问题。

There should be one – and preferably only one – obvious way to do it.