【环境】
-
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。
【解码错误的处理方式】
-
错误:
1
|
UnicodeDecodeError:
'gb18030'
codec can't decode byte
0xff
in
position
129535
: illegal multibyte sequence
|
-
异常对象:UnicodeDecodeError
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
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)
|
* 方案二:自定义编码清洗
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
# 修理 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)
|
【相关阅读】
*** walker 的流水账 ***
本文转自walker snapshot博客51CTO博客,原文链接http://blog.51cto.com/walkerqt/2054425如需转载请自行联系原作者
RQSLT