Python 2.X: Почему я не могу правильно обрабатывать Unicode? - PullRequest
7 голосов
/ 08 марта 2012

Я экспериментировал некоторое время с Python 2.X и юникодом. Но я дошел до того, что это не имеет смысла.

Первая проблема:

Какой-то код четко объяснит, что я имею в виду. Переменная txt здесь для имитации функции перевода pyqt4. Который возвращает QString.

# -*- coding: utf-8 -*-
from PyQt4 import QtCore
txt = QtCore.QString(u'può essere / sarà / 日本語')
txtUnicode1 = unicode(txt, errors='replace')
txtUnicode2 = unicode(txt)

Когда print () - две строки в юникоде, я получаю:

pu essere / sar / ???

può essere / sarà / 日本語

Конечно, я мог бы получить то же самое, используя QString .__ str __ () , но моя точка зрения - понимание, поэтому ради аргумента я хотел бы знать:

  • Почему ошибки = «заменить» заменяет все закодированные символы, когда это должно быть сделано только в случае ошибок?
  • Есть ли проблема с использованием Unicode (QString) для создания QString в строку в юникоде, подходящую для отображения?

Вторая проблема:

Я пытаюсь понять кодирование / декодирование.

>>> a = u'può essere / sarà / 日本'
>>> b = a.encode('utf-8')
>>> a
u'pu\xf2 essere / sar\xe0 / \u65e5\u672c'
>>> b
'pu\xc3\xb2 essere / sar\xc3\xa0 / \xe6\x97\xa5\xe6\x9c\xac'
>>> print a
può essere / sarà / 日本
>>> print b
può essere / sarà / 日本
  • Распечатывает ли печать a и b?
  • Кодированный UTF-8 должен быть полностью расшифрован? Разве я не должен печатать закодированную строку?
  • В чем разница между закодированной и декодированной строкой Unicode?

Ответы [ 2 ]

5 голосов
/ 08 марта 2012

Давайте запустим старый резерв, IDLE и посмотрим, сможем ли мы воспроизвести то, что вы видите.

IDLE 1.1.4      
>>> a = u'può essere / sarà / 日本'

Unsupported characters in input
>>> a = u'pu\xf2 essere / sar\xe0 / \u65e5\u672c'
>>> b = a.encode('utf-8')
>>> a
u'pu\xf2 essere / sar\xe0 / \u65e5\u672c'
>>> b
'pu\xc3\xb2 essere / sar\xc3\xa0 / \xe6\x97\xa5\xe6\x9c\xac'
>>> print a
può essere / sarà / 日本
>>> print b
può essere / sarà / 日本

Обратите внимание, что при печати b я вижу что-то другое. Это потому, что моя оболочка (IDLE) не интерпретирует последовательность байтов как текст UTF-8, а использует кодировку символов моей платформы, cp1252.

Давайте просто дважды проверим это.

>>> import sys
>>> sys.stdout.encoding
'cp1252'

Да, именно поэтому я веду себя иначе, чем вы. Потому что ваш sys.stdout.encoding - это UTF-8. И именно поэтому, несмотря на то, что a и b являются совершенно разными значениями, они отображают одно и то же; ваш терминал интерпретирует байты как UTF-8.

Так что вам может быть интересно, можем ли мы преобразовать нашу последовательность символов Unicode a в последовательность байтов, которая может быть отображена в IDLE

>>> c = a.encode('cp1252') 

Traceback (most recent call last):
  File "<pyshell#19>", line 1, in -toplevel-
    c = a.encode('cp1252') #uses default encoding
  File "C:\Python24\lib\encodings\cp1252.py", line 18, in encode
    return codecs.charmap_encode(input,errors,encoding_map)
UnicodeEncodeError: 'charmap' codec can't encode characters in position 20-21: character maps to <undefined>

Ответ - нет; cp1252 не поддерживает кодирование китайских символов в байтах.

5 голосов
/ 08 марта 2012

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

Различие между строками Unicode и обычными строками становится намного более ясным в Python 3, но это верно в Python 2. Unicode-строка содержит Unicode-представление строки, нормальная строка - байтовая строка, содержащая кодировку этого Unicode представление. Документация для Unicode объясняет, что происходит. При передаче errors параметр unicode действует иначе, чем когда он не передан и пытается декодировать строку. Непонятно, из какой кодировки он пытается декодировать, но он может подумать, что это utf-8, когда на самом деле это utf-16 или что-то подобное.

Оператор print кодирует строку Unicode в любую кодировку, которую использует ваш терминал. Это может быть ascii, или это может быть utf-8 или что-то еще. print a на самом деле делает print a.encode('utf-8') за кадром.

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

...