【环境】
Windows 10 x64
Python 3.6.3
【关于 gb18030 编码】
GB 18030 wiki:https://zh.wikipedia.org/wiki/GB_18030
单字节,其值从0到0x7F。
双字节,第一个字节的值从0x81到0xFE,第二个字节的值从0x40到0xFE(不包括0x7F)。
四字节,第一个字节的值从0x81到0xFE,第二个字节的值从0x30到0x39,第三个字节从0x81到0xFE,第四个字节从0x30到0x39。
【解码错误的处理方式】
错误:
UnicodeDecodeError: 'gb18030' codec can't decode byte 0xff in position 129535: illegal multibyte sequence
bytes.decode
codecs.register_error 样例
异常对象:UnicodeDecodeError
方案一:自定义 replace_errors:
import codecs
# gb18030 乱码 handler
def WalkerGB18030ReplaceHandler(exc):
print('exc.start: %d' % exc.start)
print('exc.end: %d' % exc.end)
print('exc.encoding: %s' % exc.encoding)
print('exc.reason: %s' % exc.reason)
text = ''
for ch in exc.object[exc.start:exc.end]:
print('ch:')
print(ch)
text += ('0x%02X' % ch)
return (text, exc.end)
# 注册自定义handler
codecs.register_error("myreplace", WalkerGB18030ReplaceHandler)
* 方案二:自定义编码清洗
# 修理 gb18030文件
# 将乱码转化为十六进制字符串,例如:b'\xff' 转为字符串 0xFF
# 将不可打印单字节转为十六进制字符串,例如:b'\xff' 转为字符串 0x7F
# srcFile 为原始 gb18030文件
# dstFile 为修理后的 gb18030文件
# explicit 控制是否转换为不可打印字符: explicit 为 False 是不转换(默认),否则转换
def RepairGB18030File(srcFile, dstFile, explicit=False):
with open(srcFile, mode='rb') as fin:
byteText = fin.read()
byteLength = len(byteText)
print('byteLength: %d' % byteLength)
pos = 0 # 位置
byteList = list()
# 末尾添加2对\r\n防止pos溢出
byteText += b'\x0d\x0a\x0d\x0a'
while pos < byteLength:
byte1 = bytes([byteText[pos]])
byte2 = bytes([byteText[pos+1]])
byte3 = bytes([byteText[pos+2]])
byte4 = bytes([byteText[pos+3]])
# 单字节汉字(正常)
if b'\x00' <= byte1 <= b'\x7f':
pos += 1
if byte1.decode('gb18030').isprintable(): # 可打印字符
byteList.append(byte1)
continue
if byte1 in (b'\x0d', b'\x0a'): # 换行符
byteList.append(byte1)
continue
if explicit: # 要求转换不可打印字符
byteNew = ("0x%02X" % ord(byte1)).encode('gb18030')
byteList.append(byteNew)
else: # 不要求转换不可打印字符
byteList.append(byte1)
# 多字节汉字(双字节或四字节)
elif b'\x81' <= byte1 <= b'\xfe':
#双字节(正常)
if (b'\x40' <= byte2 <= b'\x7e') or (b'\x80' <= byte2 <= b'\xfe'):
pos += 2
byteList.extend([byte1, byte2])
continue
#四字节
if b'\x30' <= byte2 <= b'\x39':
# 四字节(正常)
if (b'\x81' <= byte3 <= b'\xfe') or (b'\x30' <= byte4 <= b'\x39'):
pos += 4
byteList.extend([byte1, byte2, byte3, byte4])
continue
# 四字节乱码
pos += 1 #错误的时候只能移动一个字节
byteNew = ("0x%02X" % ord(byte1)).encode('gb18030')
byteList.append(byteNew)
continue
# 双字节乱码
#0x00-0x2f、0x7f、0xff
pos += 1 #错误的时候只能移动一个字节
byteNew = ("0x%02X" % ord(byte1)).encode('gb18030')
byteList.append(byteNew)
else:
# 单字节乱码
#应该只剩 0x80 和 0xff
byteNew = ("0x%02X" % ord(byte1)).encode('gb18030') #4个字节
pos += 1 #错误的时候只能移动一个字节
byteList.append(byteNew)
repairedText = b''.join(byteList).decode('gb18030')
with open(dstFile, mode='w', encoding='gb18030') as fout:
fout.write(repairedText)
【相关阅读】
2、汉字字符集编码查询
*** walker 的流水账 ***