最近在使用WINDOWS + PYCHARM 环境编写一些脚本时,发现print 会导致编码错误,如以下。 print接收字符串参数,python3字符串采用unicode编码。 从表面上看, 应该是在unicode编码转成GBK编码时候,发生错误。 \ufffd 这个unicode码值,没有对应的GBK编码。表面的意思是明白了,但是为什么会发生这个错误呢。花了一些时间搜索调试去理解这个问题。
UnicodeEncodeError: 'gbk' codec can't encode character '\ufffd' in position 0: illegal multibyte sequence
首先确定一下问题:
在WINDOWS + PYCHARM环境下创建 python文件
# -*- coding: utf-8 -*- import sys import sys import io print("\ufffd")
"\ufffd".encode("gbk") # 编码错误
"\ufffd".encode("utf-8") # 编码正确
执行结果:
Traceback (most recent call last):
File "C:/Users/GV-USER11/PycharmProjects/pythonProject/test_encode.py", line 6, in <module>
print("\ufffd")
UnicodeEncodeError: 'gbk' codec can't encode character '\ufffd' in position 0: illegal multibyte sequence
print("\ufffd") 和 "\ufffd".encode("gbk") 都会直接出发此异常。
print()函数在把字符串(unicode编码)写到标准输出IO(stdout)时,首先会将unicode编码转为系统默认的编码。 这个过程如果把字符串(unicode编码)写到文件的过程是一样的。不过在打开文件文件时,可以指定文件的编码方式(如下)。 如何指定
with open("test.json", mode='w+', encoding='utf-8') as f: f.write('\ufffd')
解决方法一:
通过pcharm--> File---> 搜索File Encoding, 设置Project Encoding, 没有找到这两个选项的确切说明,但通过测试,发现Project Encoding 可以改变 sys.stdout.encoding.
Project Encoding 设置为GBK
print(sys.stdout.encoding)
结果为 GBK
Project Encoding 设置为utf-8
print(sys.stdout.encoding)
结果为 utf-8
设置Project Encoding 为utf-8 之后, 上述异常程序可以正常运行。
print("\ufffd") 可以通过
'\ufffd' unicode编码对应的字符显示为下面的字符。
�
解决方法二: 通过代码设置 标准输出IO sys.stdout 的 encoding.
首先在pychram--> File 设置 File Encoding 的 project encoding 编码改回为 GBK
print(sys.stdout.encoding) sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') # 设置标准输出的编解码方式为utf-8
print(sys.stdout.encoding)
可以通过sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8'),设置标准输出的encoding, 设置前后打印 sys.stdout的编码方式, 发现encoding 从 GBK 更改为 utf-8. 更改后 执行 print("\ufffd")不会报异常。
进一步分析:
除了标准输出设备(sys.stdout)的编解码方式,还有输出终端的编码方式。比如pycharm自带的IDE终端,或者windows自带的cmd(cmd 默认的编码格式为GBK, 可以通过命令chcp查看)
如果只是设置了标准输出(sys.stdout)的编解码格式为utf-8, 没有设置显示终端(IDE或者CMD)只是避免了产生unicode编码异常,UnicodeEncodeError。但是终端显示仍然会显示乱码。
通过“解决方法一”, 会同时设置sys.std的编解码格式 和 pycharm自带的IDE终端的编解码格式。所以不会显示乱码? 如果“通过解决方法二”,仅仅改变了sys.stdout的编解码格式,虽然避免了异常,但是仍然会显示乱码?
所以,好的做法,保持 sys.stdout 和 显示终端 的编解码格式保持一致,并且同字符串的编码格式保持统一。