Python - кодек ascii не может декодировать байт

119

Я действительно смущен. Я пытался кодировать но ошибка сказала can't decode....

>>> "你好".encode("utf8")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)

Я знаю, как избежать ошибки с префиксом «u» в строке. Мне просто интересно, почему при вызове encode возникает ошибка «невозможно декодировать». Что делает Python под капотом?

thoslin
источник

Ответы:

167
"你好".encode('utf-8')

encodeпреобразует объект Unicode в stringобъект. Но здесь вы вызвали его для stringобъекта (потому что у вас нет u). Так питона должен преобразовать stringв unicodeобъект первым. Таким образом, это эквивалент

"你好".decode().encode('utf-8')

Но декодирование не удается, потому что строка недействительна ascii. Вот почему вы получаете жалобу на невозможность декодирования.

Уинстон Эверт
источник
50
Так какое же решение? Особенно, если у меня нет строкового литерала, у меня просто строковый объект.
Jon Tirsen
2
@JonTirsen, вы не должны кодировать строковый объект. Строковый объект уже закодирован. Если вам нужно изменить кодировку, вам нужно декодировать ее в строку Unicode, а затем закодировать как желаемую кодировку.
Уинстон Эверт
20
Итак, чтобы четко изложить это сверху, вы можете"你好".decode('utf-8').encode('utf-8')
deinonychusaur
5
@WinstonEwert Думаю, я был сбит с толку. Бизнес по кодированию обычно сбивает меня с толку. Я предполагаю, что моя путаница возникла из-за моей собственной проблемы, заключающейся в том, что я не знал, является ли ввод строкой или строкой Unicode и какой кодировкой он может быть.
deinonychusaur
@deinonychusaur, да ... я понял.
Winston Ewert
53

Всегда кодируйте из юникода в байты.
В этом направлении вы можете выбрать кодировку .

>>> u"你好".encode("utf8")
'\xe4\xbd\xa0\xe5\xa5\xbd'
>>> print _
你好

Другой способ - декодировать байты в юникод.
В этом направлении вы должны знать, что такое кодировка .

>>> bytes = '\xe4\xbd\xa0\xe5\xa5\xbd'
>>> print bytes
你好
>>> bytes.decode('utf-8')
u'\u4f60\u597d'
>>> print _
你好

Этот момент нельзя переоценить. Если вы не хотите играть в юникод "бей крота", важно понимать, что происходит на уровне данных. Здесь это объясняется по-другому:

  • Объект Unicode уже декодирован, вы никогда не захотите его вызывать decode.
  • Объект байтовой строки уже закодирован, вы никогда не захотите его вызывать encode.

Теперь, увидев .encodeбайтовую строку, Python 2 сначала пытается неявно преобразовать ее в текст ( unicodeобъект). Точно так же, увидев .decodeстроку юникода, Python 2 неявно пытается преобразовать ее в байты ( strобъект).

Эти неявные преобразования - вот почему вы можете получить, когда позвонили . Это потому, что кодирование обычно принимает параметр типа ; при получении параметра происходит неявное декодирование в объект типа перед его повторным кодированием в другой кодировке. Это преобразование выбирает декодер ascii по умолчанию , что дает вам ошибку декодирования внутри кодировщика.UnicodeDecodeErrorencodeunicodestrunicode

Фактически, в Python 3 методов str.decodeи bytes.encodeвовсе не существует. Их удаление было [противоречивый] попытки избежать этой общей путаницы.

... или как там написано в коде sys.getdefaultencoding(); обычно это ascii

Wim
источник
Так вы имеете в виду, что Python декодирует байтовую строку перед кодированием?
thoslin
@thoslin точно, я добавил подробностей.
wim
Что такое _, и почему в ваших операторах печати отсутствуют круглые скобки?
NoBugs
1
@NoBugs 1. в REPL _относится к предыдущему значению 2. потому что это вопрос python-2.x.
wim
40

Вы можете попробовать это

import sys
reload(sys)
sys.setdefaultencoding("utf-8")

Или

Вы также можете попробовать следующее

Добавьте следующую строку вверху вашего .py файла.

# -*- coding: utf-8 -*- 
Дадасо Занзане
источник
8

Если вы используете Python <3, вам нужно сообщить интерпретатору, что ваш строковый литерал является Unicode, добавив к нему префиксu :

Python 2.7.2 (default, Jan 14 2012, 23:14:09) 
[GCC 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> "你好".encode("utf8")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)
>>> u"你好".encode("utf8")
'\xe4\xbd\xa0\xe5\xa5\xbd'

Дополнительная литература : Unicode HOWTO .

Johnsyweb
источник
4
Если вы кодируете строку, почему она выдает ошибку декодирования?
MxLDevs
3

Вы используете u"你好".encode('utf8')для кодирования строки в Юникоде. Но если вы хотите изобразить "你好", вы должны это расшифровать. Как:

"你好".decode("utf8")

Вы получите то, что хотите. Возможно, вам стоит узнать больше о кодировании и декодировании.

Qingtian
источник
3

Если вы имеете дело с Unicode, иногда вместо этого encode('utf-8')вы также можете попытаться игнорировать специальные символы, например

"你好".encode('ascii','ignore')

или как something.decode('unicode_escape').encode('ascii','ignore')предложено здесь .

Не особенно полезно в этом примере, но может работать лучше в других сценариях, когда невозможно преобразовать некоторые специальные символы.

В качестве альтернативы вы можете рассмотреть возможность замены определенного символа, используяreplace() .

kenorb
источник
1

Если вы запускаете интерпретатор python из оболочки в Linux или аналогичных системах (BSD, не уверен в Mac), вам также следует проверить кодировку по умолчанию для оболочки.

Вызовите locale charmapиз оболочки (не интерпретатора Python), и вы должны увидеть

[user@host dir] $ locale charmap
UTF-8
[user@host dir] $ 

Если это не так, и вы видите что-то еще, например

[user@host dir] $ locale charmap
ANSI_X3.4-1968
[user@host dir] $ 

Python будет (по крайней мере, в некоторых случаях, таких как мой) унаследовать кодировку оболочки и не сможет печатать (некоторые? Все?) Символы Unicode. Собственная кодировка Python по умолчанию, которую вы видите и контролируете с помощью sys.getdefaultencoding()иsys.setdefaultencoding() в этом случае игнорируется.

Если вы обнаружите, что у вас есть эта проблема, вы можете исправить это,

[user@host dir] $ export LC_CTYPE="en_EN.UTF-8"
[user@host dir] $ locale charmap
UTF-8
[user@host dir] $ 

(Или, в качестве альтернативы, выберите любую раскладку клавиатуры, которую вы хотите, вместо en_EN.) Вы также можете отредактировать /etc/locale.conf(или любой другой файл, который управляет определением локали в вашей системе), чтобы исправить это.

0range
источник