在计算机数据处理与 Python 开发中,字符编码是避免文本乱码的核心规则,文件操作是实现数据持久化的关键手段。本文将拆解主流编码标准的逻辑,详解 Python 文件操作的规范流程,助力解决编码异常、文件读写等实际问题。


1. 字符编码

字符编码是计算机中用于将字符(如字母、数字、符号等)转换为二进制数据的规则集合,它是计算机能够理解和处理文本信息的基础。

1.1 编码的基本概念

  • 核心概念
  • 字符集:包含一系列字符的集合(如中文、英文、符号等),例如 ASCII、GB2312、Unicode。
  • 编码规则:将字符集中的字符映射为二进制数的规则(如 UTF-8 是 Unicode 字符集的一种编码规则)。
  • 核心问题:解决 “人类可识别的字符” 与 “计算机可处理的二进制数据” 之间的转换问题,让计算机能存储和传输文本信息。
  • 本质:一套 “字符→二进制” 的映射规则,确保同一字符在存储、传输和解析时保持一致,避免出现乱码

1.2 常见编码标准

常见的字符编码标准可按适用范围和发展历程分为以下几类,各有其特点和应用场景:

基础单字节编码(适用于英文及少数符号)

  • ASCII 编码
    • 最早期的编码标准,仅包含 128 个字符(英文字母、数字、标点、控制符等)。
    • 用 1 个字节(8 位,实际仅用前 7 位,范围 0-127)表示,例如 A 对应 01000001(十进制 65)。
    • 局限:无法表示非英文字符(如中文、日文),仅适用于英语环境

中文及东亚字符编码(针对汉字等东亚文字)

  1. GB2312

    • 中国国家标准,收录 6763 个简体汉字,以及拉丁字母、日文假名等。
    • 用 2 个字节表示汉字(高位字节范围 0xA1-0xF7,低位字节 0xA1-0xFE),兼容 ASCII(单字节部分)即针对中英文符号,分别对应使用单个字符和2个字符
    • 局限:未包含繁体汉字和部分生僻字
  2. GBK

    • 对 GB2312 的扩展,收录 21003 个汉字(含繁体),兼容 GB2312。
    • 仍用 2 个字节表示,是中文Windows系统的默认编码之一。
  3. GB18030

    • 目前最完整的中文编码标准,支持简体、繁体、少数民族文字及日韩汉字,兼容 GB2312 和 GBK。
    • 采用可变长度编码(1 字节、2 字节或 4 字节),覆盖更多字符。

全球统一编码(国际通用标准)

  1. Unicode(统一码)

    • 国际组织制定的字符集,目标是包含全球所有语言的字符(目前已收录超 15 万个)。
    • 为每个字符分配唯一的“码点”(如 U+4E2D 代表“中”,U+0041 代表“A”),但不直接规定存储方式。
    • 与传统的字符编码的二进制数都有对应关系
    • 本质是“字符集”,需通过具体编码规则(如 UTF-8)实现存储和传输。
  2. UTF-8(Unicode 转换格式)

    • 最流行的 Unicode 编码规则,可变长度(1-4 字节):
      • 英文字符:1 字节(兼容 ASCII),如 A 仍为 0x41
      • 中文、日文等:3 字节,如“中”编码为 0xE4B8AD
      • 生僻字符:4 字节。
    • 优势:节省存储空间(英文占 1 字节)、支持全球字符,是互联网、编程语言(如 Python 3)、操作系统的默认编码。
  3. UTF-16/UTF-32

    • UTF-16:以 2 字节或 4 字节表示字符,是 Windows 系统内部和 Java 的默认编码。
    • UTF-32:固定 4 字节表示每个字符,编码简单但空间占用大,较少用于存储和传输。
  • 英文场景:ASCII 足够,但现代系统已普遍兼容 UTF-8。
  • 中文场景:GBK/GB18030 适用于本地化需求,国际通用场景优先 UTF-8。
  • 全球通用:Unicode 字符集 + UTF-8 编码是主流选择,可避免跨语言、跨平台的乱码问题。

1.3 字符转换

Unicode字符码的创建

很多地方或老的系统、应用软件仍会采用各种各样传统的编码,这些编码各自为战,字符集不互通(比如GBK的“中”和UTF-8的“中”二进制完全不同)这是历史遗留问题。当软件被加载到内存运行时,若内存仍沿用硬盘的多样编码,不同编码的字符会因二进制冲突导致乱码。

因此,Unicode应运而生:

  • 它包含全球所有字符,作为“通用字符集”,相当于给每种语言的每个字符都分配了唯一“身份证号”(码点)。
  • 更关键的是,Unicode与所有传统编码都定义了明确的映射规则(比如GBK的“中”对应Unicode的U+4E2D,ASCII的“A”对应U+0041)。

这样一来,软件加载时,硬盘上的传统编码会先解码为Unicode(统一到内存的“通用格式”);运行过程中,内存只处理Unicode;保存时,再根据需要编码回原传统编码(写回硬盘)。通过这种“先统一、再转换”的模式,Unicode作为中间枢纽,让各种历史编码的软件能在内存中正常交互,从根源上避免了乱码。
在这里插入图片描述

UTF-8的由来

为解决多国字符共存问题,通过先转为内存中的 Unicode 二进制,再根据需要编码回原传统编码(写回硬盘)。但硬盘也直接使用 Unicode 编码的话也存在一些缺陷:

  • 空间浪费:Unicode 固定用 2 字节存字符,英文字符本只需 1 字节,大量英文场景下,存储体积、写入耗时会额外翻倍。
  • 传输低效:网络传输或硬盘写入时,冗余的字节会拖慢速度。

为解决 Unicode 落地存储/传输的“臃肿”问题,UTF-8(Unicode 转换格式) 被设计为精简方案:
它是 Unicode 的“可变长编码优化”,根据字符范围动态调整字节数:

  • 英文字符 → 1 字节(完美兼容 ASCII,不浪费空间 )
  • 中/日等字符 → 3 字节(精准适配常用字符 )
  • 生僻字符 → 4 字节(覆盖特殊需求 )
  • 存储流程为:多国字符 → 内存 Unicode 二进制 → 编码(encode)为 UTF-8 二进制 → 存储/传输
  • 读取时反向操作:UTF-8 二进制 → 解码(decode)为内存 Unicode → 显示字符
    在这里插入图片描述

内存不使用 UTF-8 的原因

UTF-8 是“存储/传输的精简格式”,但内存更需要“快速查询、统一映射”的特性

Unicode 作为“字符身份证”,在内存中用固定长度(如 2/4 字节)存储,能让程序快速定位、处理字符;而 UTF-8 的可变长特性,会增加内存中字符定位、操作的复杂度(需判断字节长度)。

简单说:Unicode 负责内存里的“统一管理”,UTF-8 负责存储/传输的“高效瘦身” ,二者分工协作,既解决多国字符兼容,又平衡空间与性能。

1.4 python解释器执行文件

之前将Python运行代码时的步骤总结如下:

  1. Python解释器首先读取.py文件中的源代码;
  2. 解释器内部的编译器组件会将源代码编译成字节码,这一过程是自动且即时发生的,对用户是透明的;
  3. Python虚拟机执行字节码指令,将字节码转换为机器码并执行,从而实现程序的运行。

执行的前两步

执行的前两步本质是 Python 解释器读取文本文件的过程。要保证读不乱码,则必须将python解释器读文件时采用的编码方式设置为文件当初写入硬盘时的编码格式,如果没有设置,python解释器则才用默认的编码方式。在python3中默认为utf-8,在python2中默认为ASCII,可以通过在文件首行指定文件头来修改默认的编码。

# -*- coding: 编码方式 -*-  

# 例如:# -*- coding: gbk -*-

必须放在文件的第一行;若第一行是 #!/usr/bin/env python(指定解释器路径),则编码声明需放在第二行:

#!/usr/bin/env python
# -*- coding: utf-8 -*-  # 第二行有效

解释器会先用默认的编码方式读取文件的首行内容,由于首行是纯英文组成,而任何编码方式都可以识别英文字符。

执行的第三步

在 Python 执行的第三个阶段(语法解析与内存分配),当处理字符串赋值语句时,核心差异体现在 Python 2 和 Python 3 对字符串在内存中的存储方式上。

  • Python 3 强制将字符串在内存中统一为 Unicode,从根源上避免了编码不匹配的问题。
  • Python 2 因历史设计,默认按文件编码存储字符串,若文件编码、操作环境编码、字符串使用场景的编码不一致,极易产生乱码,需通过 u'字符串' 显式规避,将字符串类型强制存储unicode。
  • 举例:
# -*- coding: utf-8 -*-
x = '上'               
print(x)               

# 在windows下的cmd中运行效果如下
C:\Users\Administrator>python2 E:\ceshi.py
涓

打印时,x的utf-8二进制传给终端。终端需将其解码为unicode才能显示,但解码用的是自身默认编码,windows的cmd默认gbk(不匹配,乱码)。

# -*- coding: utf-8 -*-
x = u'上' # 即便文件头为utf-8,x的值依然存成unicode

# 在windows下的cmd中运行效果如下
C:\Users\Administrator>python2 E:\ceshi.py
上

1.5 Python 中的编码处理

Python 中的编码处理核心是字符串(str)与字节(bytes)的转换及场景适配:

  • 字符串在内存中以 Unicode 形式存储,作为人类可直接读写的字符序列;
  • 字节则以二进制形式存在,用于计算机的传输与存储,且需关联具体编码(如 UTF-8 等);

二者通过编码(strbytes)与解码(bytesstr)实现转换,确保不同场景下的字符处理准确无误。

  • 编码strbytes,用 str.encode(encoding="utf-8")
    s = "中文"
    b = s.encode("utf-8")  # 编码为 UTF-8 字节
    print(b)  # 输出:b'\xe4\xb8\xad\xe6\x96\x87'
    
  • 解码bytesstr,用 bytes.decode(encoding="utf-8")
    b = b'\xe4\xb8\xad\xe6\x96\x87'
    s = b.decode("utf-8")  # 解码为 Unicode 字符串
    print(s)  # 输出:中文
    

关键原则:编码与解码必须使用相同的编码规则(如均用 UTF-8 或 GBK),否则会导致乱码。

2. 文件操作

文件是计算机系统中实现数据持久化存储与组织管理的基础单元。其本质是由一系列相关数据(如文本、图像、音频等)按特定格式组合而成的集合,核心作用是将内存中的临时数据转移到磁盘、固态硬盘等存储介质中,实现数据的长期保存。

文件的核心元素包括三部分:文件名(由主名与扩展名组成,扩展名常暗示文件类型,如.txt代表文本、.py代表Python代码);路径(用于准确定位文件在存储系统中的位置,分绝对路径与相对路径);内容(按格式可分为文本数据——基于字符编码的可读字符序列,和二进制数据——需特定程序解析的原始01序列,如图片、视频等)。

简言之,文件是连接内存临时数据与持久化存储的关键桥梁,也是计算机数据管理的基础载体。

2.1 文件基本操作

2.1.1 文件基本操作

文件的基本操作是与存储介质交互的核心能力,主要包括创建、读取/写入、关闭。

  1. 打开文件:通过函数(如 Python 的 open())建立程序与文件的连接,需指定:

    • 文件路径(绝对路径或相对路径,如 ./data.txt
    • 操作模式(读、写、追加等,决定可执行的操作)
    • 编码(文本文件需指定,如 utf-8,二进制文件无需指定)

    示例(Python):

    f = open("file.txt", "r", encoding="utf-8")  # 以读模式打开文本文件
    
  2. 操作文件:根据打开模式执行读写操作:

    • 读取:获取文件内容(如 read() 读全部、readline() 读一行)。
    • 写入:向文件添加内容(如 write("内容") 写入字符串)。
  3. 关闭文件:调用 close()释放系统资源,避免占用或损坏文件。(推荐用 with 语句自动关闭,无需手动调用 close()):

    with open("file.txt", "r") as f:  # 退出缩进块时自动关闭
        content = f.read()
    

举例:文本文件写入

# 打开文件("w" 模式,UTF-8 编码)
f = open("note.txt", "w", encoding="utf-8")
try:
    # 执行写入操作
    f.write("Hello, 世界!\n")
    f.writelines(["第一行\n", "第二行\n"])
finally:
    # 无论是否出错,都确保关闭文件
    f.close()
---------------------------------------------------------
# 写入文本,with语法(后面会进行了解)
with open("note.txt", "w", encoding="utf-8") as f:
    f.write("Hello, 世界!\n")  # 写入单行
    f.writelines(["第一行\n", "第二行\n"])  # 写入多行

2.1.2 open()函数

open() 是 Python 中用于打开文件并创建文件对象的内置函数,是文件操作的入口,其核心作用是建立程序与文件之间的连接,以便执行读写等操作。基本语法如下:

open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)

其参数涵盖了文件操作的各类配置细节:

  • file(必选参数):指定要操作的文件路径。支持绝对路径和相对路径,若文件不存在且打开模式为“写”(如 w/a),会自动创建文件;若为“读”(如 r),则抛出 FileNotFoundError 异常。
  • mode(可选参数,默认值 'r':定义文件的操作模式(包括读写权限和数据类型)。
  • buffering(可选参数,默认值 -1:设置文件缓冲策略(控制数据从内存写入磁盘的时机)。
- `0`:关闭缓冲(仅二进制模式可用),数据即时写入磁盘。
- `1`:行缓冲(仅文本模式可用),遇到换行符 `\n` 时写入磁盘。
- `>1`:指定缓冲区大小(单位:字节),缓冲区满时写入磁盘。
- `-1`(默认):使用系统默认策略(文本模式为行缓冲,二进制模式为全缓冲)。
  • encoding(可选参数,默认值 None:指定文本文件的编码格式(如 'utf-8''gbk'),仅文本模式有效,二进制模式(带 b)下不可指定,否则报错。默认编码依赖操作系统,还是建议显式指定以避免乱码。
  • errors(可选参数,默认值 'strict':定义编码/解码错误时的处理方式(仅文本模式有效)。
 - `'strict'`(默认):遇到错误直接抛出 `UnicodeError` 异常。
 - `'ignore'`:忽略错误字符(可能丢失数据)。
 - `'replace'`:用 `�` 替换错误字符。
 - `'backslashreplace'`:用 Unicode 转义序列(如 `\ufffd`)替换错误字符。
  • newline(可选参数,默认值 None:控制文本模式下换行符的读写转换规则。
- `None`(默认):读文件时,`\n`、`\r`、`\r\n` 均转换为 `\n`;写文件时,`\n` 转换为系统默认换行符(如 Windows 为 `\r\n`)。
- `''`:读文件时不转换换行符;写文件时 `\n` 不转换,直接写入。
- 具体字符(如 `'\n'`/`'\r\n'`):读文件时仅识别指定字符为换行符;写文件时 `\n` 转换为指定字符。
  • closefd(可选参数,默认值 True:控制关闭文件对象时是否同时关闭底层文件描述符(操作系统资源)。若 file 是文件路径(字符串),必须设为 True(默认),关闭文件时同时释放描述符;若 file 是文件描述符(整数),closefd=False 表示关闭文件对象后不释放描述符(需手动管理)。
  • opener(可选参数,默认值 None:自定义文件打开逻辑(高级功能)。需传入一个函数,该函数接收文件路径和模式参数,返回文件描述符。用于特殊场景(如自定义权限校验、访问非本地文件系统),普通操作无需使用。

日常使用中,file(路径)、mode(模式)、encoding(编码) 是最核心的参数,掌握这三个参数即可应对绝大多数文本和二进制文件操作。其他参数多用于特殊配置,可根据具体需求深入调整。

# 1. 文本模式读文件(默认 mode='r')
f = open("test.txt", encoding="utf-8")  # 等价于 open("test.txt", "r", encoding="utf-8")
content = f.read()
f.close()

# 2. 文本模式写文件
f = open("output.txt", "w", encoding="utf-8")
f.write("Hello, Python!")
f.close()

# 3. 二进制模式读图片
f = open("image.jpg", "rb")  # 二进制只读,无需 encoding
img_bytes = f.read()
f.close()
  • 注意事项
  1. 资源释放:打开文件后必须调用 f.close() 关闭,否则会占用系统资源。推荐用 with 语句自动管理(with open(...) as f: ...)。
  2. 路径错误:若 file 路径不存在且模式为读(r/rb),会抛出 FileNotFoundError
  3. 模式匹配:写入操作(w/a 等)需确保模式支持,否则报错(如用 r 模式调用 write())。

2.1.3 with语法 自动关闭文件

在 Python 中,with 语法(也称 “上下文管理器”)是处理文件操作的推荐方式,其核心优势是自动管理文件资源—— 无论操作是否正常完成(或是否抛出异常),都会在代码块执行结束后自动关闭文件,避免手动调用 close() 遗漏导致的资源占用、文件损坏等问题。

  • 基本语法
with open(...) [as 变量名]:
    # 缩进块内使用资源
    操作语句...
  • 原理
  1. 执行 with open(...) as f 时,先调用 open() 函数创建文件对象 f,同时触发上下文管理器的 __enter__() 方法(准备资源)。
  2. 执行 with 缩进块内的代码(操作资源)。
  3. 无论缩进块内是否报错(如读写失败、语法错误),都会自动触发上下文管理器的 __exit__() 方法,在该方法中会调用文件对象的 close() 函数(释放资源)。
  • 基本用法(自动关闭文件)

文本文件读写(自动关闭)

# 写入文件:缩进块结束后自动关闭文件
with open("test.txt", "w", encoding="utf-8") as f:
    f.write("用 with 语法写入,无需手动 close()\n")
    f.writelines(["第一行\n", "第二行\n"])

# 读取文件:同样自动关闭
with open("test.txt", "r", encoding="utf-8") as f:
    content = f.read()
    print(content)  # 正常打印内容,文件已在缩进结束后自动关闭

二进制文件操作(自动关闭)

# 复制图片:读写操作结束后,两个文件对象均自动关闭
with open("source.jpg", "rb") as read_f, open("copy.jpg", "wb") as write_f:
    img_bytes = read_f.read()  # 读取源图片二进制数据
    write_f.write(img_bytes)   # 写入新图片

多文件同时操作:可通过逗号分隔多个 open() 语句,在一个 with 块内同时管理多个文件,均会自动关闭(如上文“ 二进制文件操作”示例)。

  • 其他资源管理

with 语法 除了和open() 函数配合操作文件外,还可用于数据库连接、网络套接字等:

# 示例:数据库连接(伪代码)
with db.connect() as conn:  # 假设 db.connect() 返回上下文管理器
    cursor = conn.cursor()
    cursor.execute("SELECT * FROM table")  # 操作数据库
# 离开块后,连接自动关闭

2.1.4 文本文件和二进制文件

文本文件和二进制文件是文件的两种不同的展现形式。

  • t表示文本模式,默认的,可以不体现出来。读写都是以字符串(unicode)为单位。只能针对文本文件且必须指定字符编码,即必须指定encoding参数;
  • b表示二进制模式,读写都是以bytes为单位,可以针对所有文件且一定不能指定字符编码,即一定不能指定encoding参数

这两种模式都不能单独进行使用,必须配合r/w/a这些操作模式使用。

# 文本模式写文件(隐含 t,显式指定 encoding)
with open("test.txt", "w", encoding="utf-8") as f:
    f.write("Hello, 文本模式!")  # 写入 str 类型

# 文本模式读文件(隐含 t)
with open("test.txt", "r", encoding="utf-8") as f:
    content = f.read()  # 读取结果为 str 类型
-----------------------------------------------------
# 二进制模式复制图片(显式 b,不指定 encoding)
with open("source.jpg", "rb") as f_read:
    img_bytes = f_read.read()  # 读取结果为 bytes 类型

with open("copy.jpg", "wb") as f_write:
    f_write.write(img_bytes)  # 写入 bytes 类型

2.2 文件操作模式

文件操作模式是 Python 中 open() 函数的核心参数(mode),用于指定文件的操作权限(读 / 写 / 追加等)和数据类型(文本 / 二进制),所有模式均需组合使用。

2.2.1 操作模式介绍

  • w:(只写模式):文件不存在则创建文件,存在的话则打开清空内容,并且将文件指针放在文件的开头。

  • r:(只读模式):文件不存在则报错,存在的话则打开文件,并且将文件指针放在文件的开头。

  • a:(追加模式):文件不存在则创建文件,存在的话则打开文件,并且将文件指针放在文件的末尾。

  • x:(首创只写模式,较少使用):文件已存在则报错,不存在的话则创建文件,将文件指针放在文件的开头。适合需要确保文件 “首次创建” 的场景(如生成唯一日志、避免重复写入等)。

wx 模式的区别:

  • w 模式:如果文件已存在,会清空原有内容并覆盖(危险操作,可能丢失数据)。
  • x 模式:如果文件已存在,会直接报错(更安全,适合明确需要“新建”而非“覆盖”的场景)。
  • +:(读写模式,组合使用):需与基础权限模式(r/a/w/x)组合使用,赋予文件 “可读且可写” 的双重权限。

在 Python 文件操作中,r/a/w/x 是基础权限模式,+ 是扩展符(表示“读写权限”),它们的组合决定了文件的操作行为。以下是系统梳理:

模式 文件存在时 读写权限 写入位置 典型场景
r 保留内容 只读 不支持写入 读取配置文件
w 清空内容 只写 覆盖整个文件 生成新报告(覆盖旧报告)
a 保留内容 只写 末尾追加 记录日志(新增内容到最后)
x 报错 只写 新文件开头 创建唯一文件(防重复)
r+ 保留内容 读写 光标位置覆盖 修改文件中间内容
w+ 清空内容 读写 新内容覆盖 新建文件并需要读写验证
a+ 保留内容 读写 强制末尾追加 先读历史数据,再补充新数据
x+ 报错 读写 新文件开头 安全创建并立即读写新文件

2.2.2 文件操作详解

文本模式(默认 t,操作 str,需指定 encoding

  1. r(文本只读)

场景:读取已存在的文本文件(文件不存在则报错)。

# 假设已有文件 test.txt,内容为 "Hello, 文本模式!"
try:
    with open("test.txt", "r", encoding="utf-8") as f:
        content = f.read()  # 读取全部内容
        print("文件内容:", content)
except FileNotFoundError:
    print("文件不存在!")

# 运行结果:
# 文件内容: Hello, 文本模式!
  1. w(文本覆盖写)

场景:新建文本文件或覆盖已有文件(清空原有内容)。

# 若 test.txt 已存在,会清空内容;若不存在,创建新文件
with open("test.txt", "w", encoding="utf-8") as f:
    f.write("用 w 模式覆盖写入\n")  # 写入字符串
    f.writelines(["第一行\n", "第二行"])  # 写入字符串列表

# 运行后 test.txt 内容:
# 用 w 模式覆盖写入
# 第一行
# 第二行
  1. a(文本追加写)

场景:向文本文件末尾补充内容(不覆盖原有内容)。

# 向已有的 test.txt 末尾追加内容
with open("test.txt", "a", encoding="utf-8") as f:
    f.write("\n追加的第三行")

# 运行后 test.txt 内容:
# 用 w 模式覆盖写入
# 第一行
# 第二行
# 追加的第三行
  1. x(文本独占创建)

场景:安全创建新文件(文件已存在则报错,避免意外覆盖)。

try:
    # 仅当 new_file.txt 不存在时才创建并写入
    with open("new_file.txt", "x", encoding="utf-8") as f:
        f.write("这是独占创建的文件")
    print("文件创建成功!")
except FileExistsError:
    print("文件已存在,无法重复创建!")

# 首次运行结果:文件创建成功!(生成 new_file.txt)
# 再次运行结果:文件已存在,无法重复创建!
  1. r+(文本读写,覆盖式)

场景:读取并修改文件中间内容(写入会覆盖光标位置内容)。

# 先读取文件,再修改中间内容
with open("test.txt", "r+", encoding="utf-8") as f:
    # 读取前10个字符(光标移到第10位)
    print("读取前10字符:", f.read(10))  # 输出:读取前10字符:用 w 模式覆盖
    # 写入内容,覆盖光标后的字符
    f.write("【修改后的内容】")

# 运行后 test.txt 内容(原"写入"被覆盖):
# 用 w 模式覆盖【修改后的内容】
# 第一行
# 第二行
# 追加的第三行
  1. a+(文本读写,追加式)

场景:先读取文件全部内容,再向末尾追加新内容(写入只能在末尾)。

with open("test.txt", "a+", encoding="utf-8") as f:
    # 光标初始在末尾,需移到开头才能读全部内容
    f.seek(0)  # 移动光标到文件开头
    all_content = f.read()
    print("原有内容:\n", all_content)
    
    # 追加新内容(无论光标在哪,写入都在末尾)
    f.write("\n用 a+ 模式追加的内容")

# 运行后 test.txt 末尾会新增一行:用 a+ 模式追加的内容
  1. w+(文本读写,覆盖创建)

场景:新建/覆盖文件后,需同时读写(需移动光标才能读取)。

with open("temp.txt", "w+", encoding="utf-8") as f:
    # 先写入内容(光标在末尾)
    f.write("w+ 模式写入的内容")
    # 光标在末尾,直接读为空,需移到开头
    f.seek(0)
    read_content = f.read()
    print("读取到的内容:", read_content)  # 输出:读取到的内容:w+ 模式写入的内容

二进制模式(加 b,操作 bytes,禁止 encoding

  1. rb(二进制只读)

场景:读取图片、视频等二进制文件(获取原始字节数据)。

# 读取一张图片的二进制数据
with open("image.jpg", "rb") as f:
    img_bytes = f.read()  # 结果为 bytes 类型
    print("图片字节数:", len(img_bytes))  # 输出图片大小(如 102400 字节)
  1. wb(二进制覆盖写)

场景:生成或覆盖二进制文件(如复制图片、保存二进制数据)。

# 复制图片(读取源图片字节,写入新文件)
with open("image.jpg", "rb") as read_f, open("image_copy.jpg", "wb") as write_f:
    img_data = read_f.read()
    write_f.write(img_data)  # 写入 bytes 数据

print("图片复制完成!")  # 运行后生成 image_copy.jpg
  1. xb(二进制独占创建)

场景:安全创建二进制新文件(避免覆盖已有文件)。

try:
    # 仅当 new_image.png 不存在时创建
    with open("new_image.png", "xb") as f:
        # 假设已有一张图片的 bytes 数据(此处用示例数据)
        sample_bytes = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR...'  # PNG 文件头示例
        f.write(sample_bytes)
    print("二进制文件创建成功!")
except FileExistsError:
    print("二进制文件已存在!")
  • 文本模式:操作 str,加 encoding="utf-8",适合 .txt/.py 等可阅读文件。
  • 二进制模式:操作 bytes,加 b 且不加 encoding,适合媒体、压缩包等文件。
  • 权限选择:读用 r,覆盖写用 w,追加用 a,安全创建用 x,读写结合加 +

2.3 文件操作的常用方法

2.3.1 读取方法

  1. read(size=-1):读取文件内容,返回字符串(文本模式)或字节(二进制模式),size 为可选整数,指定读取的字符数(文本)或字节数(二进制);默认 -1 表示读取全部内容。
with open("test.txt", "r", encoding="utf-8") as f:
    print(f.read(5))  # 读取前5个字符
    print(f.read())   # 读取剩余所有内容
  1. readline(size=-1):读取一行内容(包括换行符 \n),光标移到下一行开头,size 限制读取的最大字符/字节数,默认读取整行。
with open("test.txt", "r", encoding="utf-8") as f:
    print(f.readline())  # 读第一行
    print(f.readline())  # 读第二行
  1. readlines(hint=-1):读取所有行,返回列表(每行作为列表元素,包含换行符),hint 为可选整数,指定大约读取的字符/字节总数(超过则停止)。
with open("test.txt", "r", encoding="utf-8") as f:
    lines = f.readlines()
    print(lines)  # 输出:['第一行\n', '第二行\n']

read()readlines()都是将内容一次性读入内容,如果内容过大会导致内存溢出,若还想将内容全读入内存,则必须分多次读入,有两种实现方式:

# 方式一
with open('a.txt',mode='rt',encoding='utf-8') as f:
 for line in f:
     print(line) # 同一时刻只读入一行内容到内存中

# 方式二
with open('1.mp4',mode='rb') as f:
 while True:
     data=f.read(1024) # 同一时刻只读入1024个Bytes到内存中
     if len(data) == 0:
         break
     print(data)

2.3.2 写入方法

  1. write(string/bytes):写入内容(文本模式传字符串,二进制模式传字节),返回写入的字符/字节数。
# 文本模式
with open("test.txt", "w", encoding="utf-8") as f:
    count = f.write("写入一行内容\n")
    print(f"写入了 {count} 个字符")  # 输出:写入了 8 个字符(含换行符)

# 二进制模式
with open("data.bin", "wb") as f:
    f.write(b"binary data")  # 写入字节
  1. writelines(iterable):写入可迭代对象(如列表、元组)中的所有元素(文本模式为字符串,二进制为字节)。不会自动添加换行符,需手动在元素中包含 \n
with open("test.txt", "w", encoding="utf-8") as f:
    lines = ["第一行\n", "第二行\n", "第三行"]
    f.writelines(lines)  # 写入列表中的所有字符串

2.3.3 其他常用方法

  1. close():关闭文件,释放系统资源(with 语句会自动调用,手动操作时需显式调用)。

  2. flush():强制将缓冲区内容写入磁盘(无需关闭文件),一般用在实时写入日志、确保数据即时保存等场景。在刷新缓冲区之前,读写的数据是保留在缓冲区中,并没有真正的写到文件中。

with open("log.txt", "a", encoding="utf-8") as f:
    f.write("重要日志...")
    f.flush()  # 立即写入磁盘,避免程序崩溃丢失数据
  1. readable():判断文件对象是否处于 “可读模式”。
  2. writable():判断文件对象是否处于 “可写模式”。
# readable()
with open('a.txt',mode='rb') as f:
	res = f.readable()
	print(res)    # True
    
# writable()
with open('a.txt',mode='wb') as f:
	res = f.readable()
	print(res)   # False

2.4 文件操作扩展

2.4.1 控制文件指针的移动

在文件操作中,“文件指针”(或称为光标)用于标记当前读写操作的位置。通过控制指针移动,可实现随机读写(如修改文件中间内容、重复读取某部分数据等)。

Python 中主要通过 seek() 方法控制指针移动,配合 tell() 方法获取当前指针位置,实现精准操作。

指针移动的单位都是以bytes/字节为单位,但是 t 模式下的 read(n),n代表的是字符个数。

with open('aaa.txt',mode='rt',encoding='utf-8') as f:
	res=f.read(4)
	print(res)  # abcd,只读取了4个字符。由此验证t模式下的read(n),n代表的是字符个数
  • 指针移动方法seek()

seek() 是文件对象的核心方法,用于精确控制文件指针(光标)的位置,实现随机读写(而非仅从开头顺序操作)。

file.seek(offset, whence=0)

offset:偏移量(整数)

  • 正数:向文件末尾方向(末尾)移动
  • 负数:向文件开头方向(开头)移动

whence:基准位置(默认值 0)

  • 0(os.SEEK_SET):以文件开头为基准(所有模式都支持)
  • 1(os.SEEK_CUR):以当前指针位置为基准(仅二进制模式支持)
  • 2(os.SEEK_END):以文件末尾为基准(仅二进制模式支持)

注意:只有0模式可以在t下使用,1、2必须在b模式下用。

# 0模式详解:参照物是文件开头位置
----------------------------------------------------------
# a.txt用utf-8编码,内容如下(abc各占1个字节,中文“你好”各占3个字节)abc你好

with open('a.txt',mode='rt',encoding='utf-8') as f:
    f.seek(3,0)     # 参照文件开头移动了3个字节
    print(f.tell()) # 查看当前文件指针距离文件开头的位置,输出结果为3
    print(f.read()) # 从第3个字节的位置读到文件末尾,输出结果为:你好
    # 注意:由于在t模式下,会将读取的内容自动解码,所以必须保证读取的内容是一个完整中文数据,否则解码失败
 
with open('a.txt',mode='rb') as f:
    f.seek(6,0)
    print(f.read().decode('utf-8')) #输出结果为: 好

----------------------------------------------------------
# 1模式详解:参照物是当前指针所在位置
----------------------------------------------------------
with open('a.txt',mode='rb') as f:
    f.seek(3,1) # 从当前位置往后移动3个字节,而此时的当前位置就是文件开头
    print(f.tell()) # 输出结果为:3
    f.seek(4,1)     # 从当前位置往后移动4个字节,而此时的当前位置为3
    print(f.tell()) # 输出结果为:7

----------------------------------------------------------
# 2模式详解:参照物是文件末尾位置,应该倒着移动,一般是负数。
----------------------------------------------------------
with open('a.txt',mode='rb') as f:
    f.seek(0,2)     # 参照文件末尾移动0个字节, 即直接跳到文件末尾
    print(f.tell()) # 输出结果为:9
    f.seek(-3,2)     # 参照文件末尾往前移动了3个字节
    print(f.read().decode('utf-8')) # 输出结果为:好
  • 获取文件指针的当前位置tell()

tell() 是文件对象的内置方法,用于返回当前文件指针(光标)的位置,即下一次读写操作开始的位置。

file.tell()
  • 返回值:整数,表示当前指针位置(单位取决于文件模式)。
  • 核心作用:获取指针位置,用于判断当前读写进度或配合 seek() 实现精确移动。
# a.txt:测试123456
with open('a.txt',mode='rb') as f:
	f.seek(6,0)  
	print(f.tell())     # 6
	f.seek(7,0)
	res=f.read()
	print(res.decode('utf-8'))   # 23456

tell()seek() 配合,可实现 “移动指针→操作→确认位置” 的闭环,是文件随机读写的核心组合。

2.4.2 文件内容修改(主要针对文本文件)

在 Python 中修改文件内容的核心挑战是:文件对应的是硬盘空间,硬盘不能修改,对应着文件本质也不能修改。即文件无法直接在原地插入或修改中间内容(写入操作会覆盖原有数据)。

因此,需根据文件大小和修改需求选择合适的方法,常用方案如下:

① 小文件修改:读取→修改→重写(推荐)

适用于文件体积较小(如几 MB 以内),原理是将文件内容全部读入内存,修改后重新写入原文件(覆盖)。

  1. r 模式读取文件内容到内存(字符串或列表)。
  2. 在内存中修改内容(如替换字符串、修改特定行)。
  3. w 模式打开文件,将修改后的内容重新写入(覆盖原文件)。
# 原文件 test.txt 内容:"I like apple. apple is good."
with open("test.txt", "r", encoding="utf-8") as f:
    content = f.read()  # 读取全部内容到内存

# 在内存中修改(将 "apple" 替换为 "orange")
new_content = content.replace("apple", "orange")

# 重新写入原文件(覆盖)
with open("test.txt", "w", encoding="utf-8") as f:
    f.write(new_content)

# 修改后内容:"I like orange. orange is good."
② 大文件修改:逐行处理→写入临时文件(内存友好)

适用于大文件(如几十 MB 以上),避免一次性加载全部内容占用过多内存。

  1. 打开原文件(r 模式)和一个临时文件(w 模式)。
  2. 逐行读取原文件,修改目标行后写入临时文件。
  3. 完成后删除原文件,将临时文件重命名为原文件名称。
import os

# 原文件 large_file.txt 中,将包含 "error" 的行标记为 "[ERROR]"
with open("large_file.txt", "r", encoding="utf-8") as f_old, \
     open("temp.txt", "w", encoding="utf-8") as f_new:
    
    for line in f_old:  # 逐行读取(内存友好)
        if "error" in line.lower():
            # 修改目标行
            new_line = f"[ERROR] {line}"
            f_new.write(new_line)
        else:
            # 非目标行直接写入
            f_new.write(line)

# 删除原文件,将临时文件重命名为原文件
os.remove("large_file.txt")
os.rename("temp.txt", "large_file.txt")
③ 注意事项
  1. 重写原文件前建议备份(尤其大文件),避免修改过程中程序崩溃导致数据丢失;
  2. 临时文件法更安全(原文件保留至修改完成);
  3. 无论文本还是二进制模式,写入操作都是从当前指针位置开始覆盖,而非插入。若需插入内容,必须通过“读取→修改→重写”实现。
  4. 文本文件修改时,需确保读写的 encoding 参数一致(如均用 utf-8),避免乱码。
  5. 逐行处理大文件时,避免使用 readlines()(一次性加载),改用 for line in f 迭代读取。

通过本文的学习,我们既能理解编码从 ASCII 到 UTF-8 的演进逻辑,掌握 Python 中编码 / 解码的统一规则,也能熟练运用open()函数、with语法等工具,根据文件类型与需求选择合适的操作方案。后续开发中,只需牢记 “编码一致防乱码、模式匹配合需求” 的原则,即可高效处理文本与文件相关任务,持续提升数据处理能力。


Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐