UnicodeEncodeError 原理与解决方案

在进行写入转码时报错如下:

UnicodeEncodeError: 'gbk' codec can't encode character '\u2022' in position 106: illegal multibyte sequece

是因为部分编码不支持该种编码格式导致的。常见于特殊字符·这里先讲原理,需要解决方案的直接看后面部分,总结解决方案是通过自带的chardet库自动识别中文的编码并转码。

原理:GB2312 < GBK < GB18030

中文编码的发展历程新增了很多汉字和表情,现在的emoj表情包和特殊符号大多基于GB18030来实现的,因此如果使用旧的编码无法理解新的字符。从而出现报错

GB18030又分为GB18030-2000和GB18030-2005;

GB 2312

6763 个汉字,其中一级汉字 3755 个,二级汉字 3008 个;同时收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的 682 个字符。

  • GB 2312 的出现,基本满足了汉字的计算机处理需要,它所收录的汉字已经覆盖中国大陆99.75% 的使用频率。
  • 对于人名、古汉语等方面出现的罕用字,GB 2312 不能处理,这导致了后来 GBK 及 GB 18030 汉字字符集的出现。

GBK

21886 个汉字和图形符号,包括:

  • GB 2312 中的全部汉字、非汉字符号。
  • BIG5 中的全部汉字。
  • 与 ISO 10646 相应的国家标准 GB 13000 中的其它 CJK 汉字,以上合计 20902 个汉字。
  • 其它汉字、部首、符号,共计 984 个。

GBK 向下与 GB 2312 完全兼容,向上支持 ISO 10646 国际标准,在前者向后者过渡过程中起到的承上启下的作用。

GBK 采用双字节表示,总体编码范围为 8140-FEFE 之间,首字节在 81-FE 之间,尾字节在 40-FE 之间,剔除 XX7F 一条线。GBK 编码区分三部分

GB 18030

汉字70244个, 全称为国家标准 GB 18030-2005《信息技术中文编码字符集》,2005是 GB 18030-2000《信息技术信息交换用汉字编码字符集基本集的扩充》的修订版。

GB 18030 与 GB 2312-1980 和 GBK 兼容,共收录。

  • 与 UTF-8 相同,采用多字节编码,每个字可以由 1 个、2 个或 4 个字节组成。
  • 编码空间庞大,最多可定义 161 万个字符。
  • 支持中国国内少数民族的文字,不需要动用造字区。
  • 汉字收录范围包含繁体汉字以及日韩汉字

重现编码错误

这里手动模拟将高版本的字符转换为早期版本不兼容的字符。例如以下例子中,特殊字符•是GBK不支持的字符, 所以在encoding时异常;

#-*-coding:utf-8-*-
import sys,os

#特殊字符加重号。 可以在符号大全中找到。
testStr="阿尔法•法尔阿"
with open("test.txt", "w", encoding="GBK") as fs:
    fs.write(testStr)

特殊字符:

UnicodeEncodeError 原理与解决方案_第1张图片

 运行结果如下(报错):

Traceback (most recent call last):
  File "test-code.py", line 6, in 
    fs.write(testStr)
UnicodeEncodeError: 'gbk' codec can't encode character '\u2022' in 
position 3: illegal multibyte sequence

如果需要编码正常通过,应更改为以下编码

#-*-coding:utf-8-*-
import sys,os

testStr="阿尔法•法尔阿"
with open("test.txt", "w", encoding="GB18030") as fs:
    fs.write(testStr)

解决方案: 自动识别源编码

通常在读取文件时报错编码异常。 是因为open函数默认使用计算机的编码去读取文本。常见为GBK或者UTF-8。 如需特殊指定编码读取时。需指定encoding参数;

可通过chardet库获取到文件的编码,通过源文件的编码读取不容易出错

import os,chardet
def readfile(_fname, spchar="\r\n"):
    contents = []
    if not os.path.exists(_fname):
        return
    with open(_fname, 'rb') as f:
        data =  f.read()
        #获取被读取文件的编码
        fcode = chardet.detect(data)['encoding']
        #当编码为默认时,读取为None
        fcode = "" if not fcode else fcode
       code = data.decode(fcode)  #fcode不能等于None
       contents = cont.split(spchar)
   return contents 

文本转码写入

open写入时也可通过encoding参数指定编码。 回顾文章开头部分, 低字符集不兼容高字符集的内容。否则会转码异常

def writefile(_nfname, _conts, _encode="GB18030"):
    try:
        Content = "\r\n".join(_conts)
        with open(_nfname, 'w', encoding=_encode) as f:
            f.write(Content)
    except UnicodeEncodeError:
        print("转码失败")
    except Exception as e:
        print("其他异常:%s" % str(e))    

本文应该适用于其他编程语言的类似问题。python有自动识别编码的chardet库,可以比较好的处理"未知"编码的问题。

你可能感兴趣的:(Python,python)