连接字符串时出现 UnicodeDecodeError

UnicodeDecodeError when concatenating strings

我有以下 Python 2.7 脚本:

#!/usr/bin/python
# -*- coding: utf-8 -*-

import geoip2.database

def ret_country_iso(ip):
    reader = geoip2.database.Reader('/usr/local/geoip/GeoLite2-Country.mmdb')
    response = reader.country(ip)
    return response.country.iso_code.lower()

result = ret_country_iso("8.8.8.8")
print result
result += "Роман"
print result

如您所见,我首先找出“8.8.8.8”IP 所在的国家/地区(这个 returns“我们”- 见下文)然后我将一个短字符串连接到它包含一些俄语字符。

结果:

# ./script.py
us
Traceback (most recent call last):
   File "./script.py", line 12, in <module>
    result += "Роман"
UnicodeDecodeError: 'ascii' codec can't decode byte 0xd0 in position 0: ordinal not in range(128)

现在,如果我改为尝试以下操作

#!/usr/bin/python
# -*- coding: utf-8 -*-

result = "us"
print result
result += "Роман"
print result

然后一切正常:

./script.py 
us
usРоман

显然,'ret_country_iso()' 函数 returns 与字面上的“us”字符串有所不同,我的 Python 太差了。

如何更正上述内容?

编辑:根据 snakecharmerb 的建议,以下作品:

#!/usr/bin/python
# -*- coding: utf-8 -*-

import geoip2.database

def ret_country_iso(ip):
    reader = geoip2.database.Reader('/usr/local/geoip/GeoLite2-Country.mmdb')
    response = reader.country(ip)
    return response.country.iso_code.lower().encode('utf-8')

result = ret_country_iso("8.8.8.8")
print result
result += "Роман"
print result

Python2没有严格区分unicode和bytes,所以两种类型拼接的结果不一致:

u'abc' + 'def'

成功,但是

u'US' + 'Роман'

导致异常。通常的方法——“Unicode 三明治”模式——是在应用程序的边缘解码和编码字符串类型的数据,并且只在应用程序内使用 unicode(对于主要处理字节的应用程序,采用反向模式)。

因此,在组合 strunicode 实例时,您可以选择以下任一选项:

# unicode result
u'US ' + 'Роман'.decode('utf-8')

# str result
u'US '.encode('utf-8') + 'Роман'

但关键是要在整个代码中保持一致,否则最终会出现很多错误。

Python 3 对两种类型的区分更加严格;如果可能的话,你应该考虑使用它来更好地处理 unicode,因为 Python 2 不再受支持。