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兆字节。在计算机领域,常用于描述大型文件、程序或数据集的大小,例如硬盘容量、内存容量等。)

相关推荐
仪器科学与传感技术博士16 分钟前
python:前馈人工神经网络算法之实战篇,以示例带学,弄明白神经网络算法应用的思路、方法与注意事项等
人工智能·python·深度学习·神经网络·算法·机器学习
java1234_小锋2 小时前
【NLP舆情分析】基于python微博舆情分析可视化系统(flask+pandas+echarts) 视频教程 - 微博舆情数据可视化分析-热词情感趋势树形图
python·信息可视化·自然语言处理
宸津-代码粉碎机3 小时前
LLM 模型部署难题的技术突破:从轻量化到分布式推理的全栈解决方案
java·大数据·人工智能·分布式·python
都叫我大帅哥3 小时前
当数据流经LangChain时,RunnablePassthrough如何成为“最懒却最聪明”的快递员?
python·langchain
都叫我大帅哥3 小时前
机器学习界的“钢铁侠”:支持向量机(SVM)全方位指南
python·机器学习
柴 基6 小时前
Jupyter Notebook 使用指南
ide·python·jupyter
Python×CATIA工业智造7 小时前
Pycaita二次开发基础代码解析:几何体重命名与参数提取技术
python·pycharm·pycatia
你的电影很有趣7 小时前
lesson30:Python迭代三剑客:可迭代对象、迭代器与生成器深度解析
开发语言·python
成成成成成成果9 小时前
揭秘动态测试:软件质量的实战防线
python·功能测试·测试工具·测试用例·可用性测试
天天进步201510 小时前
Python游戏开发引擎设计与实现
开发语言·python·pygame