Python 文本文件和二进制文件有什么区别?encoding 和 newline 参数有什么作用?
简化版
文本文件读写的是 str,Python 会按 encoding 做字节和字符串之间的转换;二进制文件读写的是 bytes,不做编码转换。newline 控制文本模式下换行符如何识别和写出,跨平台处理 CSV、协议文本时尤其重要。
详细版
文本 I/O 和二进制 I/O 的核心区别是“有没有解码/编码层”。
with open("a.txt", "r", encoding="utf-8") as f:
text = f.read() # str
with open("a.txt", "rb") as f:
data = f.read() # bytes
文本模式下:
- 读文件:磁盘字节按照
encoding解码成str; - 写文件:
str按照encoding编码成字节; - Python 可能对换行符做通用换行处理;
- 如果编码不匹配,可能抛
UnicodeDecodeError或产生乱码。
二进制模式下:
- 读写对象是
bytes; - 不进行字符编码处理;
- 不进行文本换行转换;
- 适合图片、压缩包、网络协议二进制包、加密数据等。
newline 参数常见用法:
open("data.txt", "r", encoding="utf-8", newline=None) # 默认,通用换行
open("data.txt", "r", encoding="utf-8", newline="") # 保留换行细节,常用于 csv
open("data.txt", "w", encoding="utf-8", newline="\n") # 写出统一 LF
面试中可以说:文本模式关心字符语义,二进制模式关心原始字节;只要内容不是纯文本,就优先二进制。
完整版教学
一、文件在磁盘上本质都是字节
操作系统层面没有“真正的字符串文件”。磁盘保存的是一串字节,比如:
E4 BD A0 E5 A5 BD
如果按 UTF-8 解码,它是“你好”;如果按错误编码解释,就可能变成乱码。Python 文本模式帮我们做的就是这层转换。
所以文本 I/O 的关键问题不是“能不能读”,而是“按什么编码读”。默认编码受运行环境影响,在 Windows、Linux、容器、CI 中可能不同。工程里处理文本文件,尤其是中文内容,建议显式指定:
with open("note.txt", "r", encoding="utf-8") as f:
content = f.read()
二、为什么二进制文件不能用文本模式随便读
二进制文件没有字符语义。图片、PDF、zip、音视频中的字节组合不一定是合法文本编码。
如果用文本模式读取:
with open("logo.png", "r", encoding="utf-8") as f:
data = f.read()
可能直接出现 UnicodeDecodeError。即使没有报错,也可能因为换行转换导致数据变化。对二进制文件来说,一个字节变了,文件就可能损坏。
正确写法是:
with open("logo.png", "rb") as f:
data = f.read()
三、encoding 解决的是字节和字符的映射
encoding 参数只在文本模式下有意义。常见编码包括 utf-8、gbk、latin-1 等。
读文件时编码不匹配:
with open("gbk_file.txt", "r", encoding="utf-8") as f:
print(f.read())
如果文件实际是 GBK,按 UTF-8 读就可能失败。面试里可以补充 errors 参数:
open("bad.txt", "r", encoding="utf-8", errors="replace")
errors="replace" 会用替代字符处理无法解码的字节,适合“尽量读出来做排查”的场景,但不适合严肃数据处理,因为它会丢失精确信息。
四、newline 解决的是换行符差异
不同系统历史上使用过不同换行表示:
- Unix/Linux/macOS 常用
\n; - Windows 常用
\r\n; - 老式 Mac 曾使用
\r。
文本模式默认会启用通用换行识别。读文件时,常见换行可能被统一转换成 \n。这对普通文本很方便。
但 CSV 是典型例外。Python 的 csv 模块建议打开文件时使用 newline="",让 csv 模块自己处理换行。如果不这么做,在某些平台上写 CSV 可能出现空行或换行不符合预期。
import csv
with open("users.csv", "w", encoding="utf-8", newline="") as f:
writer = csv.writer(f)
writer.writerow(["id", "name"])
五、怎么判断该用文本还是二进制
可以问自己一个问题:我操作的是“字符内容”还是“原始字节”?
- 配置文件、日志、Markdown、JSON、CSV:通常是文本;
- 图片、音频、视频、压缩包、加密数据:通常是二进制;
- 网络响应体:如果已知编码并要解析文本,可以解码;如果要保存原始内容,用二进制。
很多 bug 都来自“看起来像文本,其实边界不清”。比如 HTTP 响应、第三方上传文件、用户导出的 CSV,最好明确编码和换行策略。
六、常见误区与追问
记忆钩子:磁盘上永远是字节;文本模式帮你把字节解码成字符串,二进制模式让你直接面对 bytes。编码错了,字符就会乱;模式错了,数据可能被改。
- 误区:文本文件和二进制文件在磁盘上本质不同。 磁盘上都是字节,区别在于程序是否按某种编码把字节解释为字符。
- 追问:为什么图片、压缩包不能用文本模式处理? 文本模式会尝试解码并可能做换行转换,二进制数据不一定是合法文本,强行处理可能报错或破坏内容。
- 误区:不写 encoding 也没问题。 默认编码依赖运行环境,跨机器可能不同;处理文本文件尤其是中文内容时应显式写
encoding="utf-8"或实际编码。 - 追问:
errors="ignore"是否推荐? 它会静默丢掉无法解码的字节,可能掩盖数据损坏;排查问题时更适合先用严格模式或replace看见异常字符。 - 误区:所有系统换行符都是
\n。 Windows 文本文件常见\r\n,Unix 常见\n;文本模式可做通用换行处理,但 CSV 等格式有时要把 newline 控制权交给库。 - 追问:为什么
len("中".encode("utf-8"))不是 1? 字符数和字节数不同,中文字符在 UTF-8 中通常占 3 个字节;这也是文本随机 seek 比二进制更复杂的原因。
七、加强记忆
文本模式面对的是 str,核心是编码和换行;二进制模式面对的是 bytes,核心是保持原始字节不变。读中文文本要显式指定 encoding,处理 CSV 要留意 newline="",处理图片压缩包等非文本内容要坚定使用 rb / wb。