·
日常编程 ·
48916
Python中文乱码本质是编码与解码不匹配,核心解决法是读写文件时显式指定正确encoding参数;常见情形包括UTF-8带BOM需用'utf-8-sig'、GBK文件应选'gbk'、不确定时可用chardet探测或按'utf-8-sig','utf-8','gbk','latin-1'顺序尝试。
乱码不是文件“坏了”,而是 Python 用错了钥匙去开锁。绝大多数情况,open() 没写 encoding 参数,或者写了但和文件真实编码不匹配——直接指定正确编码就能解决,不用绕弯。
为什么加 encoding='utf-8' 还是乱码
常见原因不是 UTF-8 错了,而是文件实际带 BOM 或根本就不是 UTF-8:
-
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 0:基本是文件为 UTF-8 with BOM,但用了 encoding='utf-8'(它不自动跳过 BOM);换成 encoding='utf-8-sig' 即可
- 开头出现
\ufeff 字符:同上,BOM 被当作文本内容读进来了
- 报错含
byte 0x81 或 0xa3:大概率是 GBK/GB2312 文件,该用 encoding='gbk'
- 纯英文内容能读、中文一出现就崩:说明编码选对了但文件本身混了非法字节(比如剪贴时带入了控制字符),不是编码问题,是数据质量问题
不确定编码时怎么安全试出来
别靠猜,用最小代价验证。优先级建议:看来源 → 查 BOM → 用 chardet 快速探测 → 尝试常见编码列表
- 先检查是否有 BOM:用十六进制编辑器或命令行
xxd -l 4 file.txt,看到 ef bb bf 就是 UTF-8 with BOM
-
chardet 只读前 10KB 足够:import chardet; chardet.detect(open('file.txt', 'rb').read(10000))['encoding'];注意它对短文本或全 ASCII 文件可能返回 ascii,不可靠
- 稳妥的 fallback 列表(按顺序尝试):
['utf-8-sig', 'utf-8', 'gbk', 'gb18030', 'latin-1'];其中 latin-1 几乎总能解码(不报错),但中文会变乱码,仅用于“先读出来看看内容是否合理”
encoding='utf-8-sig' 和 'utf-8' 的区别
关键在 BOM 处理逻辑,不是“更高级”的 UTF-8:
Python 3.14.3
微软官方的 Python 扩展,是 VS Code 安装量最高的扩展(209M+)。集成 IntelliSense(通过 Pylance)、调试(通过 Python Debugger)、代码检查、格式化、重构和单元测试等功能。支持 Jupyter Notebook、虚拟环境管理和多 Python 版本切换。
-
utf-8-sig:Python 自动识别并跳过开头的 0xef 0xbb 0xbf,解码后字符串不带 \ufeff;对无 BOM 的 UTF-8 文件完全兼容
-
utf-8:严格按 UTF-8 规则解码,遇到 BOM 就把它当普通字符塞进字符串开头,后续做 .strip() 或正则替换都可能误伤合法内容
- Windows 记事本另存为“UTF-8”默认带 BOM;VS Code / PyCharm 默认保存为 UTF-8 without BOM;所以只要文件来源不确定,优先用
utf-8-sig
写入文件时也必须显式指定 encoding
只管读不管写,等于给下一个人挖坑:
- 写入中文,必须写
open(..., 'w', encoding='utf-8');不写参数,Windows 下默认用 cp936(GBK),Linux/macOS 可能是 utf-8,结果不可控
- 如果下游系统明确要求 GBK(如某些老旧 Windows 工具),才用
encoding='gbk';但建议写完立刻用 iconv -f gbk -t utf-8 转成 UTF-8 存档
- 避免用
errors='ignore' 或 'replace' 掩盖问题:它们会让乱码“看起来正常”,但原始信息已丢失,调试时反而更难定位
BOM 和编码探测只是表象,真正容易被忽略的是“一致性”——编辑器保存用什么编码、代码里 open() 写什么编码、终端显示用什么编码,三者差一个环节,乱码就会冒头。处理时别只盯一个点,顺手检查 VS Code 右下角编码显示、PyCharm 的 File Encodings 设置、以及 chcp 输出(Windows 命令行),比反复改 encoding 参数更省时间。
Python类中定义的方法默认会自动接收实例对象作为第一个参数,若方法签名未包含self,调用时就会报“多传入1个位置参数”的错误——本质是Python隐式传入了实例,而函数未声明接收。 python类方法定义中必须显式声明`self`参数 在Python中,**所有实例方法(即定义在类内部、用于操作实例数据的方法)都必须将`self`作为第一个形参**。这不是约定俗成,而是语言强制要求:当通过实...
带参数的装饰器必须三层:外层接收参数并返回装饰器,中层接收函数,内层接收调用参数;不加@functools.wraps会导致__name__、__doc__等元信息丢失,影响调试、反射和IDE功能;参数校验应放在外层。 为什么带参数的装饰器必须用三层函数 因为 Python 的 @ 语法只接受一个可调用对象作为装饰器,而你传进去的 @log(level="DEBUG") 实际上是先执行 log(l...
本文介绍针对20GB级CSV文件的内存友好型流式处理方案,通过csv.DictReader逐行解析、aiohttp并发控制与XML模板填充,实现低内存占用、高吞吐量的HTTP批量提交。 本文介绍针对20gb级csv文件的内存友好型流式处理方案,通过`csv.dictreader`逐行解析、`aiohttp`并发控制与xml模板填充,实现低内存占用、高吞吐量的http批量提交。 处理超大CSV文件(...
read_csv读取GBK文件报错需指定encoding='gbk',若失败可试'gb2312'或'gb18030';注意BOM干扰,可用'utf-8-sig'处理UTF-8 BOM;避免非法编码名,优先固定编码策略而非依赖chardet。 read_csv读取GBK文件报错:'utf-8' codec can't decode byte 这是最常见现象——pandas.read_csv 默认用 ...
os.listdir()返回指定路径下一级子项(文件和文件夹)的名称列表,故len()统计的是条目总数而非纯文件数;需配合os.path.isfile(os.path.join(path, f))过滤才能准确计数。 os.listdir() 返回的是什么,为什么不能直接用 len() 统计子目录里的文件数 os.listdir() 只返回指定路径下**一级子项的名称列表**(包括文件和文件夹),不...
np.loadtxt读含字符串CSV报错因默认全转float,解决需用结构化dtype显式定义各列类型与名称,如dtype=[('name','U10'),('age','i4'),('height','f4')],并指定encoding='utf-8'。 为什么 np.loadtxt 读含字符串的CSV会报错“could not convert string to float” 因为 np.lo...
Python 3 中 tkFont 已被移除,需改用 tkinter.font;Tkinter 不支持直接加载本地字体文件,必须先通过系统安装字体,再用 font.families() 确认名称后使用。 tkFont 在 Python 3 中已不存在,改用 font 模块 Python 3.0 起 tkFont 模块被移除,所有字体操作统一由 tkinter.font 模块提供。如果你在代码里写了...
Django的DatabaseRouter仅支持分库,无法自动分表,因其只在查询前选择数据库别名,不干预SQL生成与表名替换,分表需手动处理建表、查询、写入及迁移。 分库分表不能靠 Django 的 DatabaseRouter 完全实现,它只负责“路由到库”,不处理“表拆分逻辑”;真正要分表(比如按时间/用户ID哈希),必须手动干预查询、写入和迁移流程。 为什么 DatabaseRouter 只...
TOML 是人类可维护、类型明确、无缩进风险的配置首选,tomli/tomllib 专用于只读解析;JSON 不支持注释且语法严格,YAML 缩进敏感且类型推断易错,二者在特定场景(如前端消费、K8s)仍不可替代。 tomli 不是 JSON 或 YAML 的替代品,它只解析 TOML —— 所以“用 tomli 替代 json 或 yaml”本身是个伪命题。真正该问的是:什么时候该选 TOML ...
RotatingFileHandler 日志不按大小切分是因为仅在写入新日志且当前文件超 maxBytes 时触发滚动,非定时检查;需确保有日志输出、maxBytes 不宜过小(建议≥1024)、多进程须用 QueueHandler。 RotatingFileHandler 为什么日志不按大小切分 常见现象是配置了 maxBytes 和 backupCount,但日志始终只写入一个文件,从不生成 ...