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

8

我在我的.py文件中遇到了这个问题:"UnicodeDecodeError: 'utf8'编解码器无法解码第8-13位字节:不支持的Unicode代码范围"

if line.startswith(u"Fußnote"):

该文件以utf-8编码保存,编码位于文件顶部: # -- coding: utf-8 --

我有许多其他py文件,其中包含utf-8编码的中文文本,例如在注释和数组中:arr = [u"chinese text",],所以我想知道为什么特定情况下我的代码无法工作。


你怎么确定它确实是utf-8编码?如果在Unix系统下,你可以查看文件的适当输出od -t c - Raph Levien
1
大多数GNU/Linux发行版都有hexdump -C,而FreeBSD则有hd(1)。 - YasirA
谢谢大家提供的好答案,我已经搬家了,离开了我正在使用的Windows电脑。我曾因此苦恼不已,但现在我终于理解Unicode了。等我回去后,我会接受一个答案。 - user317033
5个回答

10

让我们仔细检查一下这个错误信息:

"UnicodeDecodeError: 'utf8'编解码器无法解码第8-13个字节: 不支持的Unicode代码范围"

请注意,它说"第8-13个字节"——那是一个6字节UTF-8序列。在古老的时代可能有效,但自Unicode在21位时被冻结后,最大长度为四个字节。UTF-8验证和错误报告最近已经得到加强; 顺便问一下,你正在运行哪个版本的Python?

至少在2.7.1和2.6.6中,该错误变得更有用:"... can't decode byte XXXX in position 8: invalid start byte",其中XXXX只能是0xfc或0xfd,如果旧消息建议6字节序列。在ISO-8859-1或cp1252中,0xfc表示U+00FC LATIN SMALL LETTER U WITH DIAERESIS(也称为u-umlaut,是一个可能的嫌疑人);0xfd表示U+00FD LATIN SMALL LETTER Y WITH ACUTE(不太可能)。

问题并不在于源文件中的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对象转换为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
>>>

感谢您更新Python的提醒,以及详细的解释。 - user317033
嗨,马克...感谢您的点赞和接受答案。请注意,我们仍然不知道是什么导致了这个问题;如果您能回答@MarkTolonen的两个问题(请在编辑您的问题时回答,不要在评论中回答),那对未来的读者会很有帮助。 - John Machin

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')

-1 有两个原因:(1)您没有重现那个(不寻常的)错误消息,您会得到普通的变体(2)在调用startswith时,unicode对象没有被转换为字符串[您的意思是str]对象。请参阅我的答案。 - John Machin

3
该错误表明line不是Unicode字符串。在X.startswith(Y)中,X和Y必须是Unicode或字节串。如果混合使用,则会出现UnicodeDecodeError错误。请使用print repr(line)检查它。另外,您是否已更改site.py以将默认编码从'ascii'更改为'utf8'?通常情况下,Python 2.x的默认编解码器是'ascii'。

2

没有看到你的代码,无法确定问题是代码还是代码读取的数据文件。

当你打开文件时,你是这样做的吗:

file = open("essay.txt")

或者:

import codecs
file = codecs.open("essay.txt", encoding="utf-8")

What does:

print file.encoding

如果您将其添加到open行正下方,它会起作用吗?

以下两种方式对我都有效:

# -- coding: utf-8 --

file = open("essay.txt")

print file.encoding

for line in file:
    uline = line.decode("utf-8")
    print type(uline)
    if uline.startswith(u"Fußnote"):
        print "Footnote"
    else:
        print "Other"

并且这样做:

# -- coding: utf-8 --

import codecs
file = codecs.open("essay.txt", encoding="utf-8")

print file.encoding

for line in file:
    print type(line)
    if line.startswith(u"Fußnote"):
        print "Footnote"
    else:
        print "Other"

在第一种方法中,我允许Python默认以字节流打开文件,然后使用uline=line.decode("utf-8")将每行从字节流转换为Unicode字符串。
在第二种方法中,我将文件以UTF-8编码方式打开,因此当我迭代文件时,Python会返回Unicode字符串。
编辑 下面是一个简单的方法,可以用来查找文件中是否包含任何非UTF8数据。
import codecs
file = open("baduni.txt")
try:
    for char in codecs.iterdecode(file, "utf-8"):
        print char
except UnicodeDecodeError as e:
    print "error:", e

以下是使用示例:

$ echo 'ABC\0200\0101DEF' > baduni.txt
$ od -c baduni.txt
0000000   A   B   C 200   A   D   E   F  \n
0000011
$ python testuni.py
error: 'utf8' codec can't decode byte 0x80 in position 3: invalid start byte

在这个例子中,第四个字节(从0开始计数的第3个位置)是八进制200或十六进制0x80维基百科UTF-8文章显示这只能作为一个两字节序列的第二个字节有效。

0

你的文件保存在其他编码格式中,而不是UTF-8。找出文件所使用的编码格式(可能是CP1252或其他),并声明该编码格式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接