在Python文件中写入UTF-8字符串
我在我的.py文件中有一行代码,结果出现了一个错误:“UnicodeDecodeError: 'utf8' codec can't decode bytes in position 8-13: unsupported Unicode code range”。
if line.startswith(u"Fußnote"):
这个文件是用utf-8格式保存的,文件顶部有编码声明:
# -- coding: utf-8 --
我还有很多其他的.py文件,里面有用utf-8编码的中文文本,比如在注释和数组中,例如:arr = [u"中文文本",]。所以我在想,为什么这个特定的情况对我来说不行。
5 个回答
这个错误提示说明 line 不是一个Unicode字符串。在 X.startswith(Y)
这个函数中,X和Y必须都是Unicode字符串或者字节字符串。如果混用的话,就会出现UnicodeDecodeError错误。你可以用 print repr(line)
来查看它的内容。另外,你有没有修改过 site.py
文件,把默认编码从'ascii'改成'utf8'?通常情况下,Python 2.x的默认编码是'ascii'。
我可以用以下代码重现UnicodeDecodeError错误:
#!/usr/bin/env python
# -- coding: utf-8 --
line='Fußnoteno'
if line.startswith(u"Fußnote"):
print('Hi')
注意,line
是一个字符串对象,而u"Fußnote"
是一个unicode对象。因为line
是字符串对象,所以在调用startswith
时,unicode对象会被转换成字符串对象。在Python2中,默认会尝试用ascii编码来解码。由于u"ß"
无法用ascii编码解码,所以就会出现UnicodeDecodeError错误。
如果你先把line
变成unicode对象,就可以避免这个错误:
line='Fußnoteno'.decode('utf-8')
if line.startswith(u"Fußnote"):
print('Hi')
或者你也可以先把u"Fußnote"
变成字符串对象:
line='Fußnoteno'
if line.startswith(u"Fußnote".encode('utf-8')):
print('Hi')
让我们仔细看看这个错误信息:
“UnicodeDecodeError: 'utf8' 编解码器无法解码位置 8-13 的字节:不支持的 Unicode 代码范围”
注意,它提到“位置 8-13 的字节”——这实际上是一个6字节的 UTF-8 序列。在以前的时代,这可能是有效的,但自从 Unicode 定义为 21 位后,最大只能是四个字节。最近,UTF-8 的验证和错误报告变得更加严格;顺便问一下,你正在使用哪个版本的 Python 呢?
在 2.7.1 和 2.6.6 中,这个错误会变得更有用,变成“... 无法解码位置 8 的字节 XXXX:无效的起始字节”,其中 XXXX 只能是 0xfc 或 0xfd,如果旧消息暗示了一个 6 字节的序列。在 ISO-8859-1 或 cp1252 中,0xfc 代表 U+00FC 拉丁小写字母 U 带变音符(也叫 u-变音符,这是一个可能的嫌疑犯);0xfd 代表 U+00FD 拉丁小写字母 Y 带重音符(可能性较小)。
问题不在于你源文件中的if line.startswith(u"Fußnote"):
语句。如果它不是正确的 UTF-8,你在编译时就会收到消息,那个消息会以“SyntaxError”开头,而不是“UnicodeDecodeError”。无论如何,这个字符串的 UTF-8 编码只有 8 个字节,而不是 14 个。
问题在于(正如 @Mark Tolonen 指出的)“line”所指的内容。它只能是一个 str 对象。
要进一步了解,你需要回答 Mark 的问题(1)print repr(line)
的结果(2)site.py
的更改。
在这个阶段,澄清一下混合str
和unicode
对象的问题是个好主意(在很多操作中,不仅仅是a.startswith(b)
)。
除非操作被定义为产生一个str
对象,否则它不会将unicode
对象强制转换为str
。在a.startswith(b)
的情况下并不是这样。它会尝试使用默认编码(通常是 'ascii')来解码str
对象。
示例:
>>> "\xff".startswith(u"\xab")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xff in position 0: ordinal not in range(128)
>>> u"\xff".startswith("\xab")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xab in position 0: ordinal not in range(128)
此外,说“混合就会得到 UnicodeDecodeError”并不正确。很有可能str
对象在默认编码(通常是 'ascii')中是有效编码的——不会引发异常。
示例:
>>> "abc".startswith(u"\xff")
False
>>> u"\xff".startswith("abc")
False
>>>