当Unicode代码点(字符)被编码为UTF-8时,一些代码点会转换为单个字节,但许多代码点则需要多个字节来编码。标准的7位ASCII范围内的字符将被编码为单个字节,但是更奇特的字符通常需要更多字节来编码。
因此,如果您将这些多字节UTF-8序列分解为单个字节,就会出现奇怪的字符。有时候这些字节会对应于普通可打印字符,但通常情况下不会,所以您会看到代替符号�。
这是一个使用版权符号©、注册商标符号®和商标符号™的简短演示,它们在UTF-8中分别被编码为2、2和3个字节。我的终端设置为使用UTF-8。
utfbytes = "\xc2\xa9 \xc2\xae \xe2\x84\xa2"
print utfbytes, len(utfbytes)
for b in utfbytes:
print b, repr(b)
uni = utfbytes.decode('utf-8')
print uni, len(uni)
输出
© ® ™ 9
� '\xc2'
� '\xa9'
' '
� '\xc2'
� '\xae'
' '
� '\xe2'
� '\x84'
� '\xa2'
© ® ™ 5
Stack Overflow的联合创始人Joel Spolsky写了一篇关于Unicode的好文章:绝对必要:每个软件开发者都必须知道的有关Unicode和字符集的最低限度(无任何借口!)
你还应该查看Python文档中的 Unicode HOWTO 文章,以及Ned Batchelder的 务实的Unicode 文章,也称为“Unipain”。
这是一个从UTF-8编码的字节串中提取单个字符的简短示例。正如我在评论中提到的那样,要正确地执行此操作,您需要知道每个字符编码为多少个字节。
utfbytes = "\xc2\xa9 \xc2\xae \xe2\x84\xa2"
widths = (2, 1, 2, 1, 3)
start = 0
for w in widths:
print "%d %d [%s]" % (start, w, utfbytes[start:start+w])
start += w
输出
0 2 [©]
2 1 [ ]
3 2 [®]
5 1 [ ]
6 3 [™]
顺带一提,这是那段代码的Python 3版本:
utfbytes = b"\xc2\xa9 \xc2\xae \xe2\x84\xa2"
widths = (2, 1, 2, 1, 3)
start = 0
for w in widths:
s = utfbytes[start:start+w]
print("%d %d [%s]" % (start, w, s.decode()))
start += w
如果我们不知道UTF-8字符串中字符的字节宽度,那么我们需要做更多的工作。每个UTF-8序列在第一个字节中编码了序列的宽度,如维基百科关于UTF-8的文章所述。
以下Python 2演示展示了如何提取该宽度信息; 它产生与前两个代码片段相同的输出。
def get_width(b):
if b <= '\x7f':
return 1
elif '\x80' <= b <= '\xbf':
raise ValueError('Bad alignment: %r is a continuation byte' % b)
elif '\xc0' <= b <= '\xdf':
return 2
elif '\xe0' <= b <= '\xef':
return 3
elif '\xf0' <= b <= '\xf7':
return 4
else:
raise ValueError('%r is not a single byte' % b)
utfbytes = b"\xc2\xa9 \xc2\xae \xe2\x84\xa2"
start = 0
while start < len(utfbytes):
b = utfbytes[start]
w = get_width(b)
s = utfbytes[start:start+w]
print "%d %d [%s]" % (start, w, s)
start += w
通常情况下,不应该需要这样做:只需使用提供的解码方法即可。
对于好奇的人,这里有一个Python 3版本的get_width
函数,以及一个手动解码UTF-8字节串的函数。
def get_width(b):
if b <= 0x7f:
return 1
elif 0x80 <= b <= 0xbf:
raise ValueError('Bad alignment: %r is a continuation byte' % b)
elif 0xc0 <= b <= 0xdf:
return 2
elif 0xe0 <= b <= 0xef:
return 3
elif 0xf0 <= b <= 0xf7:
return 4
else:
raise ValueError('%r is not a single byte' % b)
def decode_utf8(utfbytes):
start = 0
uni = []
while start < len(utfbytes):
b = utfbytes[start]
w = get_width(b)
if w == 1:
n = b
else:
n = b & (0x7f >> w)
for b in utfbytes[start+1:start+w]:
if not 0x80 <= b <= 0xbf:
raise ValueError('Not a continuation byte: %r' % b)
n <<= 6
n |= b & 0x3f
uni.append(chr(n))
start += w
return ''.join(uni)
utfbytes = b'\xc2\xa9 \xc2\xae \xe2\x84\xa2'
print(utfbytes.decode('utf8'))
print(decode_utf8(utfbytes))
输出
© ® ™
© ® ™
打印内容
,这应该证明它支持UTF-8字符集。问题只在我打印content[i]
时发生。如果您有任何想法,将会非常棒。 - Lin Ma打印内容
,这应该证明它支持UTF-8字符集。问题只会发生在我打印content[i]
时。如果您有任何想法,那就太好了。 - Lin Ma