UnicodeEncodeError: ‘ascii’ code can’t encode

“UnicodeEncodeError: ‘ascii’ codec can’t encode character u’\xa3′ in position 6: ordinal not in range(128)”

由于工作需要将数据库中的信息取出生成utf-8 编码的文件,python 程序中设置默认编码为 utf8,写入文件的时候  遇到这个错误。

环境:

Centos6.5 ,  Oracle 11.2 , Python 2.7

原因:

数据库中取出的内容编码为 gbk,程序默认编码采用 utf-8 , 字符串拼接后,写入到文件时候发现字符串中有超出128字节的非英文字符,于是报错。

处理办法:

数据库取出的信息 转换为 utf-8 再进行拼接:

#打开文件

out_file = open(output_file_name, “wb”)

# 数据库字符集为gbk,先解码再编码

row_str = str(row[0]).decode(‘gbk’).encode(‘utf-8’)

write_string = row_str

#拼接的字符串统一编码为 utf-8

write_string='[‘.encode(‘utf-8′)+write_string+’,’

write_string='[‘.encode(‘utf-8′)+write_string+’,’

#写入文件

out_file.write(write_string)

参考(来自网络):
This error occurs when you pass a Unicode string containing non-English characters (Unicode characters beyond 128) to something that expects an ASCII bytestring. The default encoding for a Python bytestring is ASCII, “which handles exactly 128 (English) characters”. This is why trying to convert Unicode characters beyond 128 produces the error.

 

问题的根源:Python2 中的 string
Python 为了让其语法看上去简洁好用,做了很多 tricky 的事情,混淆 byte string 和 text string 就是其中一例。

在 Python 里,有三大类 string 类型,unicode(text string),str(byte string,二进制数据),basestring,是前两者的父类。

其实,在语言设计领域,一串字节(sequences of bytes)是否应该当做字符串(string)一直是存在争议的。我们熟知的 Java 和 C# 投了反对票,而 Python 则站在了支持者的阵营里。其实我们在很多情况下,给文本做的操作,比如正则匹配、字符替换等,对于字节来说是用不着的。而 Python 认为字节就是字符,所以他们俩的操作集合是一致的。

然后进一步的,Python 会在必要的情况下,尝试对字节做自动类型转换,例如,在上文中的 ==,或者字节和文本拼接时。如果没有一个编码(encoding),两个不同类型之间的转换是无法进行的,于是,Python 需要一个默认编码。在 Python2 诞生的年代,ASCII 是最流行的(可以这么说吧),于是 Python2 选择了 ASCII。然而,众所周知,在需要需要转换的场景,ASCII 都是没用的(128个字符,够什么吃)。
在历经这么多年吐槽后,Python 3 终于学乖了。默认编码是 Unicode,这也就意味着,做所有需要转换的场合,都能正确并成功的转换。

最佳实践
说了这么多,如果不迁移到 Python 3,能怎么做呢?
有这么几个建议:

所有 text string 都应该是 unicode 类型,而不是 str,如果你在操作 text,而类型却是 str,那就是在制造 bug。

在需要转换的时候,显式转换。从字节解码成文本,用 var.decode(encoding),从文本编码成字节,用 var.encode(encoding)。
从外部读取数据时,默认它是字节,然后 decode 成需要的文本;同样的,当需要向外部发送文本时,encode 成字节再发送。

Leave a Reply

Your email address will not be published. Required fields are marked *