Python3:读取和处理超大文件

在日常工作中,文件对象是我们常接触到的可迭代类型之一。一般用 for 循环遍历一个文件对象,可以逐行读取它的内容。但这种方式在碰到大文件时,可能会出现一些奇怪的效率问题。

需求:

小明是一位 Python 初学者,在学习了如何用 Python 读取文件后,他想要做一个小练习:计算某个文件中数字字符(0~9)的数量。

场景1:小文件处理

假设现在有一个测试用的小文件 small_file.txt,里面包含了一行行的随机字符串:

bash 复制代码
feiowe9322nasd9233rl
aoeijfiowejf8322kaf9a
...

代码示例:file_process.py

bash 复制代码
def count_digits(fname):
    """计算文件里包含多少个数字字符"""
    count = 0
   
     with open(fname) as file:
        for line in file:
            for s in line:
                if s.isdigit():
                    count += 1
    return count


fname = "./small_file.txt"
print(count_digits(fname))

运行结果:

bash 复制代码
# 运行脚本
python3 ./file_process.py

# 输出结果
13

场景2:大文件处理

假设现在我们的大文件big_file.txt,大小有5G,且所有的文本都在一行。

大文件 big_file.txt

bash 复制代码
df2if283rkwefh... <剩余 5 GB 大小> ...

却发现同样的程序花费了一分多钟才给出结果,并且整个执行过程耗光了笔记本电脑的全部 4G 内存。

问题分析:

为什么同一份代码用于大文件时,效率就会变低这么多呢?原因就藏在小明读取文件的方法里。

在代码里所使用的文件读取方式,可谓 Python 里的"标准做法":首先用 with open (fine_name) 上下文管理器语法获得一个文件对象,然后用 for 循环迭代它,逐行获取文件里的内容。为什么这种文件读取方式会成为标准?这是因为它有两个好处:

(1) with 上下文管理器会自动关闭文件描述符;

(2) 在迭代文件对象时,内容是一行一行返回的,不会占用太多内存。

不过这套标准做法虽好,但不是没有缺点。假如被读取的文件里根本就没有任何换行符,那么上面列的第 (2) 个好处就不再成立。**缺少换行符以后,程序遍历文件对象时就不知道该何时中断,最终只能一次性生成一个巨大的字符串对象,白白消耗大量时间和内存。**这就是 count_digits() 函数在处理 big_file.txt 时变得异常缓慢的原因。

要解决这个问题,我们需要把这种读取文件的"标准做法"暂时放到一边。

解决方法:

使用 while 循环加 read() 方法分块读取。

除了直接遍历文件对象来逐行读取文件内容外,我们还可以调用更底层的 file.read() 方法。与直接用循环迭代文件对象不同,每次调用 file.read(chunk_size), 会马上读取从当前游标位置往后 chunk_size 大小的文件内容,不必等待任何换行符出现。有了 file.read() 方法的帮助,优化后的代码:

bash 复制代码
def count_digits_v2(fname):
    """计算文件里包含多少个数字字符,每次读取 8 KB"""
    count = 0
    block_size = 1024 * 8
    with open(fname) as file:
        while True:
            chunk = file.read(block_size)
            # 当文件没有更多内容时,read 调用将会返回空字符串 ''
            if not chunk:
                break
            for s in chunk:
                if s.isdigit():
                    count += 1
    return count


fname = "./big_file.txt"
print(count_digits_v2(fname))

在新函数中,我们使用了一个 while 循环来读取文件内容,每次最多读 8 KB,程序不再需要在内存中拼接长达数吉字节的字符串,内存占用会大幅降低。

(吉字节是一种数据存储单位,通常用于表示大容量存储设备的容量大小。它等于1024^3(1,073,741,824)字节,或者1,024兆字节。在计算机领域,常用于描述大型文件、程序或数据集的大小,例如硬盘容量、内存容量等。)

相关推荐
2601_967980111 小时前
Python自动化办公:模块、库与样式调整教程
python·excel·自动化办公·openpyxl·样式调整
长沙三为智能科技1 小时前
GEO与传统SEO的5个关键差异:排名与被引用、关键词与答案型的全维度对比
python·aigc·seo·ai搜索·geo优化
量化分析码农3 小时前
【Python量化趋势跟踪实战 02】突破买入总被假突破坑:通道系统与入场时机
python
Agent世界3 小时前
多平台登录态巡检:cookie 判据为什么会撒谎
python
量化分析码农4 小时前
【Python量化趋势跟踪实战 01】均线多头排列就是趋势开始吗:过滤与确认
python
用户298698530144 小时前
Python 实战:把代码写入 Word 文档的两种实现方式
后端·python·api
小小张说故事4 小时前
Python 里 pickle 到底存了什么:拆开 __reduce__、__getstate__ 和那串能执行代码的字节码
python
reasonsummer4 小时前
【教学类-150-03】20261108蝴蝶连连看html(一页2个版面、3个版面)
python·html
用户019027581614 小时前
用 Polars 快速分析 A 股 K 线:比 pandas 更快的列式处理
python
用户0332126663674 小时前
使用 Python 轻松加密或解密 Excel 文档
python·excel