(一)现象
如题,报错类似:
‘gbk’ codec can’t encode character ‘\uXXYY’ in position ZZ: illegal multibyte sequence
(二)分析
2.1 确认文件编码和业务逻辑
比较奇怪的地方,这些报错的内容是从另一个文本中读取出来的,读取时指定了GBK编码且并没有错。
用其它文本工具打开看过,文件确实也不是UTF8的。
部分代码如下,下面写入时报的错:
...
dfboss = spark.read.csv(sys.argv[1] + 'zk.cm_customer*.txt', sep='|', encoding='GBK')
rddBOSS = dfboss.rdd.map(lambda r: (r[0], (r[1], r[2], r[3]))).reduceByKey(lambda x, y: x)
countBOSS = rddBOSS.count()
print("数据[营业客户]: %i" % countBOSS, flush=True)
dfhss = spark.read.csv(sys.argv[1] + 'zg.crm_customer*.txt', sep='|', encoding='GBK')
rddCRM = dfhss.rdd.map(lambda r: (r[0], (r[1], r[2], r[3]))).reduceByKey(lambda x, y: x)
countCRM = rddCRM.count()
print("数据[账务客户]: %i" % countCRM, flush=True)
...
...
f001 = open('./C_CUSTOMER_001_0.TXT', "w", encoding='GBK')
output001 = out_b_s.filter(lambda lk: lk[1][1] is None).collect()
for (kk, (v1, v2)) in output001:
try:
f001.write("%s|%s|%s|%s\n" % (kk, v1[0], v1[1], v1[2]))
g001 += 1
except Exception as err:
print(err, file=sys.stderr)
f001.close()
...
并且只有少量行报错,绝大多数是正确的。
2.2 编程就是Googleing stackoverflow……
网上搜到的解决方案大概是:
- 文件编码设置错误,排除……
- 改Python编码,Python3已经默认UTF8了。py文件和pycharm设置UTF8,排除……
- 版本?3.6和3.10现象一致,排除……
- 手动处理错误,没有针对本质,排除……
- 手动处理不能自动处理的编码,总感觉不对,排除……
2.3 继续分析
同样的数据,用Java和C++处理均无问题。
说明编码应该是正确的呀。
那么只能推断编码部分和解码,实现得不一样。
上面代码中,解码是:
dfboss = spark.read.csv(..., encoding='GBK')
编码是:
f001 = open(..., ..., encoding='GBK')
那么是不是Java和C++,以及前面的spark看到GBK的时候,判断内容是中文国标编码。
而下面的open文件方法严谨的使用了GBK编码呢?
纯猜的……
(三)解决
突然想到,GBK并不是最新的汉字编码规范,于是试了一下GB18030。
居然就OK了!
f001 = open(..., ..., encoding='GB18030')
(四)进一步分析
GBK用双字节表示,总体编码范围为 8140-FEFE,首字节在 81-FE 之间,尾字节在 40-FE 之间。
所以前面的\uE502确实不再编码范围内。
但是打开用GB18030正确输出的文本后,并没有发现\uE502,以及其它之前报的错误编码。
E5好说,这个02也太过分了,太低了吧。
再看看GB18030……
所以虽然用了GB18030就不报错了。
但问题到底是怎么产生的,是转换过程中出现了错误么?
是否读写前后的中文都是正确的没有变化呢???烦恼啊……
后来的追加测试:
试了一下用gb2312,产生了比gbk多得多的错误。
我觉得这个现象说明有字在gb2312,gbk范围外,
也间接说明\ufffd是转换中产生的错误,而不是本身字符的编码:)