在 Python 3 中,强烈推荐不要直接使用 f.readlines(),而是应该使用 f.readlines(encoding='...') 的形式,或者在打开文件时指定编码,直接使用 f.readlines() 而不指定编码,会使用系统默认编码,这在跨平台或处理非 UTF-8 文件时极易导致 UnicodeDecodeError 错误。

readlines() 是什么?
readlines() 是文件对象的一个方法,用于读取文件的所有行,并将它们作为一个列表返回,列表中的每个元素都是一行文本,包括末尾的换行符 \n。
示例:
假设有一个名为 my_file.txt 的文件,内容如下:
Hello, world!
This is the second line.
This is the third line.
使用 readlines():
# 错误示范:没有指定编码
with open('my_file.txt', 'r') as f:
lines = f.readlines()
print(lines)
输出:

['Hello, world!\n', 'This is the second line.\n', 'This is the third line.\n']
lines是一个列表。- 每个字符串元素都保留了原始的换行符
\n。
编码问题的根源:UnicodeDecodeError
计算机存储的所有文件本质上都是二进制数据(0 和 1),为了将这些二进制数据解读成我们能看懂的字符(如 'A', '中', '€'),我们需要一套“密码本”,这就是编码。
常见的编码有:
- UTF-8: 目前最通用、最推荐的编码,它可以表示世界上几乎所有语言的字符。
- GBK / GB2312: 主要用于简体中文环境。
- ISO-8859-1 (Latin-1): 一种简单的编码,无法表示中文等多字节字符。
- Windows-1252: Windows 系统早期常用的编码。
问题出在哪里?
当你用 open() 打开一个文件时,Python 需要用一种编码来“翻译”文件中的二进制流,如果你没有明确告诉 Python 使用哪种编码,它会使用系统的默认编码。

- 在中文 Windows 系统上,默认编码很可能是
gbk。 - 在 macOS 或 Linux 系统上,默认编码很可能是
utf-8。
这就导致了问题:如果你在 Windows 系统上(默认 gbk)打开一个用 UTF-8 编码保存的文件,Python 就会用错误的“密码本”去读,结果自然是乱码,甚至直接抛出 UnicodeDecodeError。
正确处理编码的几种方法
最佳实践 - 在 open() 函数中指定编码
这是最清晰、最推荐的方式,将编码作为参数传递给 open() 函数,整个文件操作过程都将是安全的。
# 推荐:在打开文件时就指定编码为 utf-8
try:
with open('my_file.txt', 'r', encoding='utf-8') as f:
lines = f.readlines()
print(lines)
except FileNotFoundError:
print("错误:文件未找到!")
except UnicodeDecodeError:
print("错误:文件编码不是 UTF-8,请尝试其他编码(如 gbk)。")
# 如果你知道文件是 GBK 编码的
# with open('my_file.txt', 'r', encoding='gbk') as f:
# lines = f.readlines()
# print(lines)
优点:
- 代码意图明确。
- 一旦编码错误,在文件打开阶段就能立即捕获,而不是在后续处理数据时才发现。
- 所有基于该文件对象的读取操作(
read(),readline(),readlines())都会自动使用正确的编码。
为 readlines() 传递 encoding 参数(不推荐)
readlines() 本身也接受一个 encoding 参数,这样做在功能上是可行的,但不推荐,因为它容易混淆,并且不如在 open() 中指定来得直观。
# 不推荐的做法
with open('my_file.txt', 'rb') as f: # 注意:这里必须用 'rb' 模式,即二进制模式
lines = f.readlines(encoding='utf-8')
print(lines)
- 为什么不推荐? 这种方式要求你先用二进制模式 (
'rb') 打开文件,readlines()再去解码,这增加了不必要的复杂性,让代码难以理解。'rb'模式下,f.readlines()返回的是包含字节串的列表,然后再由encoding参数进行解码,这个过程绕了弯路。
处理编码错误(errors 参数)
你无法确定文件的精确编码,或者文件中可能夹杂了少量错误的编码字符,这时,你可以使用 errors 参数来告诉 Python 如何处理解码错误。
errors 参数的常用值:
'strict'(默认): 遇到无法解码的字节立即抛出UnicodeDecodeError。'ignore': 忽略无法解码的字节。'replace': 将无法解码的字符替换成一个占位符(通常是 )。
示例:使用 replace
# 假设文件中有一个用 GBK 编写的 '中' 字,但文件整体是 UTF-8 编码
# 这会导致解码错误
with open('mixed_encoding.txt', 'r', encoding='utf-8', errors='replace') as f:
content = f.read()
print(content)
# 输出可能会是 "Hello �orld!" 中文字符被替换成了 �
Python 3 vs. Python 2 的关键区别
这是一个非常重要的历史知识点,能帮助你理解为什么 Python 3 对编码如此严格。
-
Python 2 (旧版)
- 文本文件和二进制文件的区分比较模糊。
open()默认使用系统的编码,这导致了大量跨平台的兼容性问题。- 字符串类型
str本质上是字节串,没有编码概念。 - 为了处理 Unicode,引入了
unicode类型,这造成了str和unicode两种类型之间的混乱,是 Python 2 最著名的痛点之一。
-
Python 3 (现代)
- 明确区分了文本和二进制。
str类型:文本字符串,它内部存储的是 Unicode 码点,与具体编码无关,这是你日常处理文本时应该使用的类型。bytes类型:字节串,它是一堆原始的字节,没有编码含义,这是处理二进制数据(如图片、网络包)时使用的类型。open()函数:- 文本模式 (
'r','w','a'):要求你提供encoding参数,它会自动在bytes和str之间进行转换。 - 二进制模式 (
'rb','wb','ab'):不处理编码,直接读写bytes对象。
- 文本模式 (
这个设计从根本上解决了 Python 2 的编码混乱问题,使得代码更加健壮和清晰。
总结与最佳实践
-
始终指定编码:在 Python 3 中处理任何文本文件时,都应在
open()函数中明确指定encoding参数。encoding='utf-8'是最安全、最现代的选择。 -
首选
with open(...):使用with语句可以确保文件在操作完成后被自动关闭,即使发生错误也是如此。 -
不要直接用
f.readlines():养成好习惯,总是写成with open('file.txt', 'r', encoding='utf-8') as f:,然后才去调用f.readlines()或其他方法。 -
处理异常:用
try...except块来捕获FileNotFoundError(文件不存在)和UnicodeDecodeError(编码错误),让你的程序更健壮。 -
理解
str和bytes:牢记 Python 3 中str是文本,bytes是二进制,这能帮助你理解文件操作的底层原理。
推荐的模板代码:
# 定义文件名和编码
FILENAME = 'your_data.txt'
ENCODING = 'utf-8' # 强烈推荐使用 utf-8
try:
# 使用 with 语句和明确的编码打开文件
with open(FILENAME, 'r', encoding=ENCODING) as f:
# 读取所有行到一个列表
lines = f.readlines()
# 遍历并处理每一行(通常可以去掉末尾的换行符)
for line in lines:
# .strip() 可以移除行首行尾的空白字符(包括 \n, \t, 空格等)
cleaned_line = line.strip()
print(f"处理行: {cleaned_line}")
except FileNotFoundError:
print(f"错误:文件 '{FILENAME}' 未找到,请检查路径。")
except UnicodeDecodeError:
print(f"错误:文件 '{FILENAME}' 的编码可能不是 '{ENCODING}'。")
except Exception as e:
print(f"发生未知错误: {e}")
