Python 读取大文件

通常面对的大文件有两种形式:

  • 多行大文件,一般里面存放的是字符串(普通文本)

  • 单行大文件,一般里面存放的是字节串(二进制流)

针对多行大文件

使用 open 的基础方法:open 打开的是一个按行迭代的可迭代对象,通过对其进行遍历,即可一行一行的读取文件,避免一次占用较大内存,底层内置了 io 缓存和内存的管理,无需使用者关心。

with 句柄作为上下文管理器,负责打开和关闭文件(包括在内部块中引发异常时)

1
2
3
4
# for line in f 会将文件对象 f 视为一个可迭代的数据类型,自动使用 IO 缓存和内存管理
with open('filename', 'rt', encoding='utf-8') as f:
for line in f:
print("do_something(line)")

针对单行大文件

上面的 open 方法解决不了的一个问题是:当大文件只有一行时该怎么办

因此,此方法借助文件对象的 read(size=1024*8) 方法,按数据的大小进行分块读取,size 的单位是比特:bytes

借助 patial 方法和 iter 方法实现这个功能:

  • functools.partial(io.read,size):用来创建一个每次被调用时从文件中读取固定数目字节的可调用对象,他接收两个参数,一个是函数,一个是函数参数,返回的是每次调用此函数的返回对象。因此,一般使用返回迭代器的函数+参数作为 partial 的函数输入。
  • iter() 函数有一个鲜为人知的特性就是,如果你给它传递一个可调用对象和一个标记值,它会创建一个迭代器。这个迭代器会一直调用传入的可调用对象直到它返回标记值为止,这时候迭代终止。在下面的例子中,iter 循环将不断返回 fp.read(block_size) 调用结果,直到其为 ‘’ 时终止,标记值 b’’ 就是当到达文件结尾时的返回值。
  • PS.如果总文件大小不是块大小的整数倍的话,最后一个返回元素的字节数会比期望值少。
1
2
3
4
5
6
7
8
9
10
11
# 生成器函数:分块读取文件内容,使用 iter+partial 函数
def chunked_file_reader(file, block_size=1024 * 8):
records = iter(partial(file.read, block_size), '')
for chunk in records:
yield chunk


# 分数据块读取文件
with open('filename', 'rb', encoding='utf-8') as f:
for chunk in chunked_file_reader(f):
print("do_something(chunk)")

知识补充:

partial 函数

如果需要减少某个函数的参数个数,你可以使用 functools.partial()partial() 函数允许你给一个或多个参数设置固定的值,减少接下来被调用时的参数个数。

partial()会固定某些参数并返回一个新的callable对象。这个新的callable接受未赋值的参数, 然后跟之前已经赋值过的参数合并起来,最后将所有参数传递给原始函数。

partial的内部机制和装饰器一样利用了函数式编程的闭包特性。简而言之,partial作为一个函数式编程中的高阶函数,其功能就是为某个已经存在的函数对象提供了一种简洁的绑定函数参数的方式。对于关键字参数,相当于提供默认值,对位置参数相当于冻结参数。

重要的是,这种参数绑定,不是在函数定义阶段(比如默认值参数),而是借助外部工具partial来进行参数的绑定,返回一个参数缩减的特化版本,这个绑定过程可以发生在编写代码的阶段(简单的得到一个特化函数),甚至可以发生在运行时,通过配置文件或者交互输入解析待绑定的参数,来动态的生成特定功能的函数。

PS. partial做到了开放封闭原则:对外开放,对内(修改)封闭

参考:https://python3-cookbook.readthedocs.io/zh_CN/latest/c07/p08_make_callable_with_fewer_arguments.html

参考:https://zhuanlan.zhihu.com/p/47124891

iter函数

iter()函数有两种用法,一种是传一个参数,一种是传两个参数。结果都是返回一个iterator对象。

所谓的iterator对象,就是有个next()方法的对象。next方法的惯例或约定(convention)是,每执行一次就返回下一个值(因此它要自己记录状态,通常是在iterator对象上记录),直到没有值的时候raiseStopIteration。

传1个参数:参数collection应是一个可迭代的集合对象,支持迭代协议(即定义有__iter__()函数),或者支持序列访问协议(即定义有getitem()函数),否则会返回TypeError异常。

传2个参数:当第二个参数sentinel出现时,参数callable应是一个可调用对象(实例),即定义了__call__()方法,当枚举到的值等于哨兵时,就会抛出StopIteration异常。

参考:https://blog.csdn.net/sxingming/article/details/51479039


Python 读取大文件
https://flepeng.github.io/021-Python-31-Python-脚本-Python-读取大文件/
作者
Lepeng
发布于
2021年4月27日
许可协议