开发者

Writing utf-8 string inside my python files

开发者 https://www.devze.com 2023-02-07 03:19 出处:网络
This line in my .py file is giving me a: \"UnicodeDecodeError: \'utf8\' codec can\'t decode bytes in position 8-13: unsupported Unicode code range\"

This line in my .py file is giving me a: "UnicodeDecodeError: 'utf8' codec can't decode bytes in position 8-13: unsupported Unicode code range"

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

The file is saved in utf-8 and has the encoding 开发者_如何转开发at the top: # -- coding: utf-8 --

I've got a lot of other py files with utf-8 encoded chinese text in them in the comments and in arrays for example: arr = [u"chinese text",] so I'm wondering why this case in particular doesn't work for me.


Let's examine that error message very closely:

"UnicodeDecodeError: 'utf8' codec can't decode bytes in position 8-13: unsupported Unicode code range"

Note carefully that it says "bytes in position 8-13" -- that's a 6-byte UTF-8 sequence. That might have been valid in the dark ages, but since Unicode was frozen at 21 bits, the maximum is FOUR bytes. UTF-8 validations and error reporting were tightened up recently; as a matter of interest, exactly what version of Python are you running?

With 2.7.1 and 2.6.6 at least, that error becomes the more useful "... can't decode byte XXXX in position 8: invalid start byte" where XXXX can be only be 0xfc or 0xfd if the old message suggested a 6-byte sequence. In ISO-8859-1 or cp1252, 0xfc represents U+00FC LATIN SMALL LETTER U WITH DIAERESIS (aka u-umlaut, a likely suspect); 0xfd represents U+00FD LATIN SMALL LETTER Y WITH ACUTE (less likely).

The problem is NOT with the if line.startswith(u"Fußnote"): statement in your source file. You would have got a message at COMPILE time if it wasn't proper UTF-8, and the message would have started with "SyntaxError", not "UnicodeDecodeError". In any case the UTF-8 encoding of that string is only 8 bytes long, not 14.

The problem is (as @Mark Tolonen has pointed out) in whatever "line" is referring to. It can only be a str object.

To get further you need to answer Mark's questions (1) result of print repr(line) (2) site.py change.

At this stage it's a good idea to clear the air about mixing str and unicode objects (in many operations, not just a.startswith(b)).

Unless the operation is defined to produce a str object, it will NOT coerce the unicode object to str. This is not the case with a.startswith(b).It will attempt to decode the str object using the default (usually 'ascii') encoding.

Examples:

>>> "\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)

Furthermore, it is NOT correct to say "Mix and you get UnicodeDecodeError". It is quite possible that the str object is validly encoded in the default encoding (usually 'ascii') -- no exception is raised.

Examples:

>>> "abc".startswith(u"\xff")
False
>>> u"\xff".startswith("abc")
False
>>>


I can reproduce the UnicodeDecodeError with this code:

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

line='Fußnoteno'
if line.startswith(u"Fußnote"):
    print('Hi')

Note that line is a string object, but u"Fußnote" is a unicode object. Since line is a string object, the unicode object is being converted to a string object in the call to startswith. In Python2, the default is to try to decode using the ascii codec. Since u"ß" can't be decoded with the ascii codec, a UnicodeDecodeError is raised.

The error can be avoided if you first make line a unicode object:

line='Fußnoteno'.decode('utf-8')
if line.startswith(u"Fußnote"):
    print('Hi')

or if you first make u"Fußnote" a string object:

line='Fußnoteno'
if line.startswith(u"Fußnote".encode('utf-8')):
    print('Hi')


The error indicates line is not a Unicode string. In X.startswith(Y) both X and Y must be Unicode or byte string. Mix and you get UnicodeDecodeError. print repr(line) to inspect it. Also have you altered site.py to change the default encoding from 'ascii' to 'utf8'? Normally it is the 'ascii' codec that is the default for Python 2.x.


Without seeing your code, it's unclear if the problem is the code or the data file the code is reading.

When you open the file, are you doing:

file = open("essay.txt")

or:

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

What does:

print file.encoding

say if you add it just below the open line?

Both of these ways work for me:

# -- 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"

and this way:

# -- 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"

In the first one, I am letting Python default to opening the file as a byte stream, then converting each line from a byte stream to a Unicode string using uline = line.decode("utf-8").

In the second one, I am opening the file as a UTF-8 encoded file, so Python returns Unicode strings when I iterate over the file.


EDIT

Here's a trivial way you could use to find out if the file contained any non-utf8 data.

import codecs
file = open("baduni.txt")
try:
    for char in codecs.iterdecode(file, "utf-8"):
        print char
except UnicodeDecodeError as e:
    print "error:", e

And an example of it in use:

$ 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

In the example, the 4th byte (position 3, counting from 0) is 200 octal/0x80 hexadecimal.
The Wikipedia UTF-8 article shows that that would only be valid as the second byte of a two-byte sequence.


Your file is saved in some other encoding, and not UTF-8. Figure out what encoding the file is in (possibly CP1252 or so), and declare that instead.

0

精彩评论

暂无评论...
验证码 换一张
取 消