2025-11-30 12:32:05

手持两把锟斤拷,

口中疾呼烫烫烫。

脚踏千朵屯屯屯,

笑看万物锘锘锘。

开发者经常这样调侃编码问题:手持两把锟斤拷,口中疾呼烫烫烫。

程序员都知道字符编码,比如 ASCII,GBK,UTF-8,那肯定能理解这些神奇的 “乱码” 出现的原因,肯定是某些地方没有正确处理编码。

那为什么总是能看见 锟斤拷,烫烫烫呢?其背后隐藏更深的原因是啥?

烫烫烫

烫,屯,葺 三个字符的频繁出现是和微软系的编译器有关,

静态分配而未初始化的内存空间,默认使用 CC 填充,CCCC 对应 GBK 编码的 烫

动态分配而未初始化的内存空间,默认使用 CD 填充,CDCD 对应 GBK 编码的 屯

动态分配然后被回收的内存空间,默认使用 DD 填充,DDDD 对应 GBK 编码的 葺

Windows 中文版本的默认编码是 GBK,一输出就显示成了 烫烫烫, 屯屯屯, 葺葺葺。

print((b'\xcc' * 6).decode('gbk'))

# 烫烫烫

print((b'\xcd' * 6).decode('gbk'))

# 屯屯屯

print((b'\xdd' * 6).decode('gbk'))

# 葺葺葺

锟斤拷

Unicode 中有个字符为 �,注意:这不是乱码!!!就是一个方块,中间为一个问号。

这个符号名字是 Replacement Character,可以翻译成替换字符或者占位字符,码位为 FFFD,十进制就是 65533。

https://unicode-table.com/en/FFFD/

按照规范,如果遇到不支持的 Unicode 字符,就转换成这个符号。

print('�'.encode('utf-8'))

b'\xef\xbf\xbd'

print('锟斤拷'.encode('gbk'))

b'\xef\xbf\xbd\xef\xbf\xbd'

print('�'.encode('utf-8').decode('gbk'))

# UnicodeDecodeError: 'gbk' codec can't decode byte 0xbd in position 2: incomplete multibyte sequence

print('�'.encode('utf-8').decode('gbk', errors='ignore'))

print('�'.encode('utf-8').decode('gbk', errors='replace'))

锟�

print('��'.encode('utf-8').decode('gbk'))

锟斤拷

从上面这个例子可以看出,如果两个字符被转换成 ��,然后被转码成 UTF-8,然后再当成 GBK 解析就会出现 锟斤拷 字样。

a = '天门'

# 原始数据

b = a.encode('gbk')

print(b)

# b'\xcc\xec\xc3\xc5'

# 模拟读数据遇到不支持的字符

c = b.decode('utf-8', errors='replace')

print(c.encode('unicode-escape'))

# b'\\ufffd\\ufffd\\ufffd\\ufffd'

# 模拟错误的编码解码操作

print(c.encode('utf-8').decode('gbk'))

# 锟斤拷锟斤拷

# =====================================

open('/tmp/aaa', 'wb').write('天门'.encode('gbk'))

open('/tmp/aaa', 'rb', encoding='utf-8', errors='replace').read()

# =====================================

print('武汉'.encode('gbk').decode('utf-8', errors='replace').encode('utf-8').decode('gbk'))

# 锟戒汉

print('火车站'.encode('gbk').decode('utf-8', errors='replace').encode('utf-8').decode('gbk'))

# 锟斤拷站

这是 Python3,默认采用 Unicode,带编码的字符(bytes 类型)只能显示出 ASCII 字符,所以最后还需要 decode 成 Unicode。

如果是 Python2,字符串默认使用执行环境字符编码,输出的时候 Unicode 也会转换成执行环境字符编码。

如果再运行在 Windows 下(默认编码为 GBK),锟斤拷的出现频率可能会更大一些。

只需要将一个 GBK 编码的文本,转换成 UTF-8,再输出,就可能见到。

C:\Windows\system32>python

Python 2.7.12 (v2.7.12:d33e0cf91556, Jun 27 2016, 15:24:40) [MSC v.1500 64 bit (AMD64)] on win32

Type "help", "copyright", "credits" or "license" for more information.

>>> a = u'天门'.encode('gbk')

>>> print(a.decode('utf-8', errors='replace').encode('utf-8'))

锟斤拷锟斤拷

锘锘锘

Windows 下 BOM 头问题(Byte order mark)。参考 BOM 头的研究。

print((u'\ufeff'.encode('utf-8') * 2).decode('gbk'))

# 锘匡豢

声明

这个和 Python 无关,和任何语言都没关系,就是开发者没有正确处理编码问题。

可能是用错误编码读了一个文件,也可能是用错误编码存了数据库。

参考资料与拓展阅读

微信公众号, 程序员的那些事, “锟斤拷” 22 元一盒?聊聊它的前世今锟斤拷

码厩, 2020-10-01, 字符编码

Copyright © 2088 次元时空特惠站 - 二次元游戏活动特区 All Rights Reserved.
友情链接