当前位置:首页>python>Python之文件读操作

Python之文件读操作

  • 2026-10-11 07:24:57
Python之文件读操作

第十章 Python之文件读操作

1. 文件的分类

无论是图片还是文档,硬盘里都是 0 和 1。唯一的区别在于:软件读取这串 0 和 1 时,用的是哪本“翻译词典”?

1.1 纯文本文件

  1. 规则 = “字符编码表”(如 UTF-8)。
  2. 存储逻辑:逐字对照编码表,把字符(字母、汉字)转为二进制;读取时反向解码为文字。
  3. 致命特征:极度“纯粹”——只存文字内容,绝对不存字体、颜色、大小等任何格式指令。
  4. 呈现结果:任何基础编辑器(记事本)都能直接打开,呈现人类可读的纯文字。
  5. 常见后缀:.txt、.py、.md、.html(源码层面仍是纯字符)。

1.2 二进制文件

  1. 规则 = “私有格式规范”(如图片压缩算法、音频采样协议).
  2. 存储逻辑:不关心字符编码,而是按照特定格式把结构体(像素点、采样率、章节偏移量)直接转为二进制。
  3. 致命特征:结构复杂`——内部数据有特定长度和位置含义(如“前4字节是宽度,后4字节是高度”).
  4. 呈现结果:必须由专用软件按协议解析,结果是图片、视频或压缩包;强行用记事本打开会变乱码。
  5. 常见后缀:.jpg、.mp3、.docx(本质是 ZIP 压缩包,属二进制)、.exe。

1.3 二者区分

用系统自带的记事本打开,保存后再关闭,文件还能正常使用吗?

  • 能正常用(内容没丢、没损坏) → 纯文本文件(因为记事本只改字符,不改结构)。
  • 完全报废(打不开、乱码或损毁) →二进制文件(因为记事本强行插入了 UTF-8 字符标记,破坏了原有的私有格式结构)。

误区:

  • 很多人误以为 .docx 或 .xlsx 是文本,因为它们有字——大错特错。它们是ZIP 压缩包(二进制),里面虽然包裹着 XML 文本片段,但整个文件必须由 Office 拆包解析,所以归属二进制。后缀永远骗人,解析规则才是唯一真理。

2. 绝对路径vs相对路径

核心本质:路径就是寻址。两者的唯一区别在于我从哪儿开始找。

2.1 绝对路径

  1. 起点 : 根目录。
  2. 从文件系统的最顶层(即“根”)出发,逐级向下,完整无遗漏地描述目标位置。
  3. 关键特征:像身份证号一样,在整台电脑中唯一且不变。
  4. 操作系统的起点差异:
    1. Windows:以盘符为根(如 C:\ 或 D:\)。
    2. Linux / macOS:以斜杠 / 为根(没有盘符概念)

2.2 相对路径

  1. 起点 :当前工作目录。
  2. 以你此时此刻所在的文件夹为参照物,描述目标的相对位置。
  3. 关键特征:像导航语音,结果随“我当前在哪”而变化。你必须搞懂两个特殊符号:
  4. .(一个点):代表当前目录(通常可省略)。
  5. ..(两个点):代表上一级目录。

2.3 二者区分

绝对路径管绝对,相对路径看相对。

  • 何时用绝对:写系统服务、定时任务(Crontab)或引用外部固定资源时。因为环境不可控,用绝对路径最稳妥,防止找不到文件。
  • 何时用相对:写项目代码时。因为项目文件夹可能会被拷贝到别人的电脑(盘符变了),使用相对路径可以保证代码零修改直接运行(即“可移植性”)。

3. Python操作文件的标准流程

标准流程成如下:

  1. 创建文件对象
  2. 操作文件(读取、写入 等)
  3. 关闭文件

Python中可以只使用两步完成:

  1. 使用 with 语句创建上下文(自动管理资源,退出即关闭,无需手动 close())。
  2. 在缩进块内操作文件。
# 正确姿势(自动关闭)
with open('test.txt', 'r', encoding='utf-8') as f:
    content = f.read()
# 退出缩进后,文件句柄自动释放

3.1 open函数

  1. file(文件路径)
  • 支持绝对路径(如 D:/data/a.txt)和相对路径(如 ./a.txt)。
  • 跨平台建议:路径字符串前加 r 防止转义,如 r'C:\data\a.txt',或用正斜杠 /(Windows 也兼容)。
  1. mode(打开模式),模式由三组互斥选项各选其一组成:
类别
选项
含义
读写主模式
r
只读
(默认)。文件必须存在,否则报错 FileNotFoundError。
w
只写
。文件存在则清空(截断),不存在则新建。危险操作,注意备份!
x
独占创建
。写模式,但文件已存在则抛出 FileExistsError,防止覆盖。
a
追加
。文件存在则末尾追加,不存在则新建。无法读取,只能写。
格式模式
t
文本模式
(默认)。读写字符串 str,需指定 encoding。
b
二进制模式
。读写字节流 bytes(如图片、音频),禁止传 encoding 参数。
附加标识
+
更新模式
(读写兼得)。搭配使用:r+(读写,指针在开头)、w+(清空读写)、a+(追加读写,指针在末尾)。

常用组合速查:

  • 'r':读文本(最常用)
  • 'w':写文本(覆盖)
  • 'a':追加文本
  • 'rb':读二进制(如图片)
  • 'wb':写二进制
  • 'r+':读写文本(修改既有文件)
  1. encoding(字符编码)
  • 仅在 t(文本模式)下有效,b 模式下传此参数会报错。
  • 铁律:永远显式指定 encoding='utf-8'。
    • 因为 Windows 默认编码是 gbk,而 Linux/macOS 是 utf-8。不指定的话,你的代码在别人电脑上大概率 UnicodeDecodeError 乱码报错。

注意事项:

  1. w 模式要当心:它上来就清空文件。如果想在原文件基础上修改,用 r+ 或先读取再写入,千万别直接用 w。
  2. seek 移动指针:用 r+ 写入时,内容是从当前指针位置覆盖写入的,不是插入。想追加直接用 a。
  3. 大文件别用 read():读取几 GB 文件时,read() 会把内存撑爆。应用 for line in f: 逐行迭代,内存友好。

4. 读取文件

4.1 read方法

概述:使用文件对象的read方法,读取文件中的内容。其本质是操作文件内部的指针,每次读取都会将指针向后移动。

方法说明:

  1. read(size)中的size是可选参数。
    1. 若不传递size参数,表示:读取文件中所有的内容(内存警告:如果文件有 5GB,这一句直接撑爆内存。仅适用于几 MB 以内的配置文件或文本!)。
    2. 若传递了size参数,表示:读取文件中指定个数的字符,或指定大小的字节。
  2. read会从上一次read的位置继续读取,若到达文件末尾后继续读取,将返回空字符串。
    1. read 会从上一次读取结束的位置继续往后读。
    2. 当指针到达文件末尾后继续调用 read,将返回空值(文本模式下为 '',二进制模式下为 b''),这是判断文件是否读完的唯一标志。
# 读取操作:使用『文件对象』的 read 方法,读取文件中的内容。
# read 方法说明:
#   1.read(size)中的 size 是可选参数。
#      若不传递 size 参数,表示:读取文件中所有的内容(注意内存占用!)。
#      若传递了 size 参数,表示:读取文件中指定个数的字符,或指定大小的字节。
#   2.read 会从上一次 read 的位置继续读取(指针思想),若到达文件末尾后继续读取,将返回空字符串。
# region
# 第一步:创建[文件对象]
file = open(file='a.txt', mode='rt', encoding='utf-8')
# file = open('a.txt', 'rt', encoding='utf-8')
# file = open('D:/test/a.txt', 'rt', encoding='utf-8')
# file = open('D:/test/girl.jpg', 'rb')
# 第二步:操作文件(读取)
# 多次调用read去逐步读取文件
# r1 = file.read(2)
# r2 = file.read(3)
# r3 = file.read(4)
# r4 = file.read()
# print(r1, end='')
# print(r2, end='')
# print(r3, end='')
# print(r4, end='')

# 用循环配合多次read(对内存友好)
while True:
    result = file.read(10)
    if result == '':
        break
    print(result, end='')

# 第三步:关闭文件
file.close()
# endregion

4.2 readline方法

 概述:使用文件对象的readline方法,读取文件中的一行。其底层依赖于换行符(\n)作为行的结束标志,读取时会将换行符一并返回。

方法说明:

  1. readline(size) 中的size是可选参数。
    1. 若不传递size参数,表示:读取当前行直到遇到换行符 \n 的所有内容(包含末尾的换行符)
    2. 若传递了size参数,表示:限制当前行最多读取的字符数(文本模式)或字节数(二进制模式)。
    3. 注意:size不是行数,而是单行内的截断长度。如果一行的长度超过 size,readline(size) 只会返回该行的前 N 个字符,指针停留在该行中间,下一次调用会继续读取该行的剩余部分,而不会跳到下一行。
  2. readline方法,同样从上一次读取结束的位置继续,当指针到达文件末尾后继续调用,返回空字符串(''),与 read() 的终止逻辑完全一致。

关于换行符的细节(容易被忽略)

关于换行符的细节(容易被忽略):

  • readline 返回的字符串包含行尾的换行符 \n。因此:
    • 使用 print(line) 会额外多打印一个空行(因为 print 自带换行)。
    • 推荐使用 print(line, end='')(示例中的写法)或 line.rstrip('\n') 来去除换行。
    • 慎用 strip():它会同时去除行首和行尾的空白字符(包括空格、制表符),可能会误删文本首尾的有意义空格。如果你的数据是纯文本且确定不需要首尾空格,用 strip() 没问题;否则请用 rstrip('\n') 只剥离换行符。
# 读取操作:使用文件对象的 readline 方法,读取文件中的一行。
# readline 方法说明:
#   1.readline(size) 中的 size 是可选参数。
#      若不传递 size 参数,表示:读取当前这一行所有的内容。
#      若传递了 size 参数,表示:表示读取当前行时,最多能读取的字符数,或字节数(size不是行数)。
#   2.readline 方法,也是从上一次位置继续读取,若到达文件末尾后继续读取,也是返回空字符串。
# region
# 第一步:创建『文件对象』
file = open('a.txt', 'rt', encoding='utf-8')

# 第二步:操作文件(读取)
# 依次调用readline逐行读取
# r1 = file.readline()
# r2 = file.readline()
# r3 = file.readline()
# r4 = file.readline()
# print(r1.strip())
# print(r2.strip())
# print(r3.strip())
# print(r4.strip())

# 通过循环配合readline逐行读取
while True:
    line = file.readline()
    if line == '':
        break
    # print(line.strip())
    print(line, end='')

# 第三步:关闭文件
file.close()

4.3 for 循环遍历文件对象

readline 是处理超大文本文件的利器,配合 while 循环能稳定控制内存峰值,且不受文件总大小影响。如果你只需要逐行遍历,直接用 for line in file: 更优雅,它是 readline 的语法糖封装。

# 读取操作:使用 for 循环直接遍历文件对象
# region
# 第一步:创建『文件对象』
file = open('a.txt', 'rt', encoding='utf-8')

# 第二步:操作文件(读取)
for line in file:
    print(line, end='')

# 第三步:关闭文件
file.close()
# endregion

4.4 readlines方法

概述:使用文件对象的readlines方法,一次性按行读完,返回一个列表。列表中的每个元素,都对应文件中的一行,且每行末尾自带换行符\n。

方法说明:

  1. readlines(hint) 中的hint是可选参数。
    1. 若不传递hint参数,表示:读取当前文件的所有行,将每一行作为列表的一个元素返回。
    2. 若传递了hint参数,表示:期望读取的字符个数 或 字节数(hint不是行数,而是字节/字符数的提示)。
  2. 返回的列表包含每行末尾的换行符 \n。处理时通常需要用 strip() 或 rstrip('\n') 去掉。
  3. 注意:由于readlines是一次性读取文件的所有内容,所以不适合读取体积较大的文件。
# 读取操作:使用文件对象的 readlines 方法,一次性按“行”读完,返回一个列表。
# readlines 方法说明:
#   1.readlines(hint) 中的 hint 是可选参数。
#       若不传递 hint 参数,表示:读取当前文件的所有行。
#       若传递了 hint 参数,表示:期望读取的【字符个数 或 字节数的上限】(hint不是行数)。
#   2.注意:由于 readlines 是一次性读取文件的所有内容,所以不适合读取体积较大的文件。
# region
# 第一步:创建『文件对象』
file = open('a.txt', 'rt', encoding='utf-8')

# 第二步:操作文件(读取)
result = file.readlines()
print(result)

# 第三步:关闭文件
file.close()
# endregion

总结:readlines 是“全量快照”工具,适合小文件快速取用;readline 和 for line in file 是流水线工具,适合大文件稳吃。数据量未知时,优先选 for line in file:,它是 Python 官方推荐的最安全做法。

四种读取方法使用场景对比表

方法
适用场景
内存特点
推荐指数
read()
小配置文件(几KB)、需要一次性获取全部内容做整体处理的场景(如JSON解析、正则全文匹配)
全部加载
,小文件快捷高效;大文件极危险,内存瞬间撑爆
⭐⭐⭐(小文件)
❌(大文件)
readline()
超大日志文件、需要手动控制循环跳出时机的场景,或处理逻辑依赖当前行号的精确控制
逐行加载
,内存占用极低(仅缓存一行),稳吃GB级文件
⭐⭐⭐⭐
readlines()
文件体量中等(几十MB以内),且后续需要通过下标随机访问某一行(如 lines[0]、lines[-1])
全部加载为列表
,每个字符串对象有额外开销,内存占用比 read() 更高
⭐⭐⭐(中等文件)
❌(大文件)
for line in file:99% 的文本逐行遍历场景
,无论文件大小,兼顾安全与简洁,是 Python 官方首选
底层使用迭代器(惰性加载),内存占与 readline()同等低,代码却更简洁
⭐⭐⭐⭐⭐(最推荐)

小文件用read(),大文件无脑上for line in file,需要下标用readlines(),需要手动控制循环用 readline()。

4.5 最佳实践

概述:更推荐使用with上下文管理器,结合for循环遍历,逐行读取文件。

# 最佳实践:使用 with 上下文管理器,结合for循环遍历,逐行读取文件。
with open('a.txt', 'rt', encoding='utf-8') as file:
for line in file:
        print(line, end='')

5. 关于with语句的扩展

概述:Python 中的with主要用于管理程序中需要成对出现的操作,例如:

  1. 上锁 / 解锁
  2. 打开 / 关闭
  3. 借用 / 归还

使用with的目标:编码者只管做具体的事,进入和离开的事,让 Python 自动处理。

# 语法格式:
with 能得到一个上下文管理器的表达式 as 变量:
    具体的事1
    具体的事2
    具体的事3

上下文管理器协议:

  1. __enter__ 方法:with中的代码执行之前调用,其返回值会赋值给as后的变量。
  2. __exit__  方法:with中的代码执行结束后调用(无论是with中否出现异常都会调用)。
# 定义一个 Person 类,让其实例对象遵循:上下文管理器协议
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def speak(self):
        print(f'我叫{self.name},年龄是{self.age}')

    def __enter__(self):
        print('-----我是进入的逻辑-----')
        return self

    # 当 with 中的代码发生异常时,__exit__ 方法的返回值规则如下:
    #   返回“真”:表示异常【已经】被处理,异常【不会】被继续抛出。
    #   返回“假”:表示异常【没有】被处理,异常【会】被继续抛出。
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('-----我是离开的逻辑-----')
        # exc_type  : 异常类型
        # exc_val   : 异常对象
        # exc_tb    : 异常追踪信息
        if exc_type:
            print(f'异常类型:{exc_type}')
            print(f'异常对象:{exc_val}')
            print(f'异常追踪信息:{exc_tb}')
        return True

# 1.计算 with 后面的表达式,得到一个『上下文管理器』。
# 2.调用『上下文管理器』的 __enter__() 方法,并将其返回值赋给 as 后面的变量。
# 3.执行 with 所管理的代码。
# 4.无论代 with 中的代码,是正常结束,还是发生异常,都会自动调用『上下文管理器』的 __exit__ 方法。

with Person('张三', 18) as p1:
    p1.speak()
# p1.study()
    print(666)

最新文章

随机文章