根本原因是操作系统默认编码、python运行环境与文件系统三者不一致,尤其windows下sys.getfilesystemencoding()返回'mbcs'(即gbk),而脚本用utf-8处理路径导致冲突;必须显式指定open()的encoding参数,优先使用pathlib.path并避免subprocess字符串拼接。

Python自动化脚本读写中文文件名报错或显示为、???、UnicodeEncodeError,根本原因不是编码“没设对”,而是操作系统默认编码、Python运行环境、文件系统三者之间不一致导致的——尤其在Windows上,sys.getfilesystemencoding()常返回'mbcs'(即GBK),而脚本却用UTF-8处理路径,冲突必然发生。
确认当前系统的文件系统编码
别猜,直接查。不同系统行为差异极大:
- Windows:
sys.getfilesystemencoding()通常返回'mbcs',实际等价于当前系统ANSI代码页(如简体中文Win10是GBK) - Linux/macOS:基本统一为
'utf-8',但若locale未正确配置(如LANG=C),也可能退化为'ascii' - 执行
python -c "import sys; print(sys.getfilesystemencoding())"看真实值,比查文档更可靠
open() 读写中文路径时必须显式指定 encoding
open() 默认使用 locale.getpreferredencoding(),它和文件系统编码经常不一致。尤其在Windows下用UTF-8字符串调用open()却不指定encoding,极易触发UnicodeEncodeError: 'charmap' codec can't encode character。
- 读取含中文路径的文件:始终加
encoding='utf-8'(如果文件内容本身是UTF-8)或encoding='gbk'(如果确定是Windows记事本保存的文本) - 写入时同理:
open('测试.txt', 'w', encoding='utf-8')—— 缺少encoding参数是90%乱码问题的根源 - 注意:
os.listdir()、pathlib.Path.iterdir()返回的路径名已经是Unicode字符串,无需解码;乱码只发生在open()或subprocess调用时
subprocess 调用外部命令时路径传参要小心
用subprocess.run()执行copy、ffmpeg、curl等命令时,中文路径会因shell编码不匹配而失败,错误信息常是FileNotFoundError或OSError: [WinError 2],实际是路径被截断或替换为?。
- Windows上优先用
shell=False(默认值),并把命令拆成列表:subprocess.run(['copy', '源文件.txt', '目标.txt'], shell=False) - 避免拼接字符串:
f'copy "{path}"'在GBK环境下遇到UTF-8路径必炸 - Linux/macOS需确保终端locale为UTF-8(
export LANG=en_US.UTF-8),否则subprocess继承的环境编码仍可能出错
跨平台脚本要避开 os.path.join 的隐式编码陷阱
os.path.join()本身不涉及编码,但它拼出的路径交给open()或subprocess时,若路径含中文且环境编码混乱,问题立刻暴露。更稳妥的做法是统一用pathlib.Path,它对Unicode更友好。
- 推荐写法:
p = pathlib.Path('data') / '用户报告.xlsx',然后p.open(encoding='utf-8') - 避免:
os.path.join('data', '用户报告.xlsx')→ 再传给open()却不设encoding - 特别注意:打包成exe(如PyInstaller)后,
sys._MEIPASS路径可能含中文,此时pathlib.Path+ 显式encoding是最稳组合
真正棘手的从来不是“怎么设UTF-8”,而是你无法控制下游工具(比如旧版ffmpeg.exe或企业内网Java服务)是否支持UTF-8路径——这时候得降级用短文件名(GetShortPathName)或临时重命名,而不是硬扛编码。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











