在Python文件中写入UTF-8字符串

8 投票
5 回答
20457 浏览
提问于 2025-04-16 10:41

我在我的.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 个回答

3

这个错误提示说明 line 不是一个Unicode字符串。在 X.startswith(Y) 这个函数中,X和Y必须都是Unicode字符串或者字节字符串。如果混用的话,就会出现UnicodeDecodeError错误。你可以用 print repr(line) 来查看它的内容。另外,你有没有修改过 site.py 文件,把默认编码从'ascii'改成'utf8'?通常情况下,Python 2.x的默认编码是'ascii'。

6

我可以用以下代码重现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')
10

让我们仔细看看这个错误信息:

“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 的更改。

在这个阶段,澄清一下混合strunicode对象的问题是个好主意(在很多操作中,不仅仅是a.startswith(b))。

除非操作被定义为产生一个str对象,否则它不会将unicode对象强制转换为stra.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
>>>

撰写回答