Windows环境Python2.7中'á'显示为'\xa0'的原因探究
- 设备:Windows
- 环境:同时安装已停止支持的Python 2.7.16与Python 3.7.1,日常使用Git Bash作为主Shell
- 基础认知:了解Python3中
unicode重命名为str,清楚Unicode代码点与字节编码的区别
在Git Bash中运行Python2.7时,输入带重音的字符得到不符合预期的结果:
Python 2.7.16 (v2.7.16:413a49145e, Mar 4 2019, 01:37:19) [MSC v.1500 64 bit (AMD64)] on win32 >>> 'á' '\xa0' # 预期为 '\xc3\xa1' >>> len('á') 1 # 预期为 2 # 参考示例 >>> 'à' '\x85' # 预期为 '\xc3\xa0'
- 使用
unicode对象可得到预期结果:
>>> u'á'.encode('utf-8') '\xc3\xa1' >>> len(u'á'.encode('utf-8')) 2
- 在IDLE中运行Python2.7得到不同结果(已知是IDLE使用Latin1编码导致):
Python 2.7.16 (v2.7.16:413a49145e, Mar 4 2019, 01:37:19) [MSC v.1500 64 bit (AMD64)] on win32 >>> 'á' '\xe1' >>> len('á') 1 >>> 'à' '\xe0'
- 默认编码是否为问题根源?当前
sys.getdefaultencoding()返回ascii,不敢修改默认编码。 - 是否为Git Bash终端编码问题?是否需要修改
PYTHONIOENCODING环境变量?Git Bash的locale显示为en_US.UTF-8。 - 改用文件执行(设置
# -*- coding: utf-8 -*-)是否可行?虽知道升级Python3可解决,但仍想探究此现象原因。
核心原因:终端输入编码与Python 2字符串处理的不匹配
在Python 2中,直接输入的'á'是字节串(str类型),而非Unicode对象。这个字节串的内容取决于终端将你输入的Unicode字符编码为字节的方式,以及Python读取这些字节时采用的编码规则。
1. Git Bash的实际编码映射问题
Git Bash在Windows上是POSIX环境模拟层,它显示的en_US.UTF-8 locale是POSIX层的设置,但实际与Python交互时,使用的是Windows传统控制台编码CP850(DOS Latin-1):
- CP850编码表中,
á对应的字节是0xA0,à对应的字节是0x85,这完全匹配你看到的输出结果。也就是说,你输入的Unicode字符á被Git Bash用CP850编码为字节\xa0,Python直接接收这个字节作为str对象,所以输出'\xa0',长度为1。
2. sys.getdefaultencoding()的作用
sys.getdefaultencoding()仅用于Python内部Unicode与字节串的自动转换(比如unicode(str)时的默认编码),它不影响终端输入输出的编码处理,所以你的问题和它无关,无需修改。
3. 解决方法与验证
方法1:设置PYTHONIOENCODING环境变量
在Git Bash中执行以下命令,告诉Python用UTF-8处理标准输入输出:
export PYTHONIOENCODING=utf-8
重新启动Python 2后,输入'á'会得到'\xc3\xa1',len('á')返回2,与预期一致。
方法2:使用Unicode对象(已验证可行)
Python 2中前缀u''创建的是Unicode对象,它会正确解析终端输入的字符为Unicode代码点,再通过encode('utf-8')转成UTF-8字节串即可得到预期结果。
方法3:通过文件执行代码
将代码写入文件并在开头添加# -*- coding: utf-8 -*-,Python会按UTF-8解析文件中的字符串,此时'á'会被解析为UTF-8字节串'\xc3\xa1',执行结果符合预期。注意此方法仅针对文件中的字符串,与终端输入场景无关。
为什么IDLE中结果不同?
IDLE基于Windows GUI实现,使用的是系统默认的GUI编码(通常为CP1252),CP1252是单字节编码,á对应的字节是\xe1,所以Python 2中'á'的输出为'\xe1',长度为1。
内容的提问来源于stack exchange,提问作者Nate Anderson

