Python: Sanitize строку для unicode?

Возможный дубликат:
Python UnicodeDecodeError - Я неправильно понимаю кодировку?

У меня есть строка, которую я пытаюсь сделать безопасным для функции unicode():

>>> s = " foo "bar bar " weasel"
>>> s.encode('utf-8', 'ignore')

Traceback (most recent call last):
  File "<pyshell#8>", line 1, in <module>
    s.encode('utf-8', 'ignore')
UnicodeDecodeError: 'ascii' codec can't decode byte 0x93 in position 5: ordinal not in range(128)
>>> unicode(s)

Traceback (most recent call last):
  File "<pyshell#9>", line 1, in <module>
    unicode(s)
UnicodeDecodeError: 'ascii' codec can't decode byte 0x93 in position 5: ordinal not in range(128)

Я в основном кручусь здесь. Что мне нужно сделать, чтобы удалить небезопасные символы из строки?

Отчасти это связано с question, хотя я не смог решить эту проблему.

Это также не удается:

>>> s
' foo \x93bar bar \x94 weasel'
>>> s.decode('utf-8')

Traceback (most recent call last):
  File "<pyshell#13>", line 1, in <module>
    s.decode('utf-8')
  File "C:\Python25\254\lib\encodings\utf_8.py", line 16, in decode
    return codecs.utf_8_decode(input, errors, True)
UnicodeDecodeError: 'utf8' codec can't decode byte 0x93 in position 5: unexpected code byte

Ответ 1

Хороший вопрос. Проблемы с кодированием сложны. Давайте начнем с "У меня есть строка". Строки в Python 2 на самом деле не являются "строками", это байтовые массивы. Итак, ваша строка, откуда она взялась и в какой кодировке? В вашем примере показаны фигурные кавычки в буквальном значении, и я даже не знаю, как вы это сделали. Я пытаюсь вставить его в интерпретатор Python или набрать его на OS X с Option- [, и он не пройдет.

Если вы посмотрите на свой второй пример, у вас есть символ hex 93. Это не может быть UTF-8, потому что в UTF-8 любой байт выше 127 является частью многобайтового последовательность. Поэтому я предполагаю, что он должен быть латинским-1. Проблема в том, что x93 не является символом в наборе символов Latin-1. Там этот "недопустимый" диапазон в латинском-1 от x7f до x9f считается незаконным. Тем не менее, Microsoft увидела этот неиспользованный диапазон и решила поставить там "кудрявые цитаты". При этом они создали подобную кодировку под названием "windows-1252", которая похожа на Latin-1 с материалом в этом недопустимом диапазоне.

Итак, допустим, что это windows-1252. Что теперь? String.decode преобразует байты в Юникод, так что тот, который вы хотите. Второй пример был на правильном пути, но он не удался, потому что строка не была UTF-8. Попробуйте:

>>> uni = 'foo \x93bar bar\x94 weasel'.decode("windows-1252")
u'foo \u201cbar bar\u201d weasel'
>>> print uni
foo "bar bar" weasel
>>> type(uni)
<type 'unicode'>

Это правильно, потому что открытие курчавой цитаты - Unicode U + 201C. Теперь, когда у вас есть Unicode, вы можете сериализовать его в байтах в любой кодировке, которую вы выбираете (если вам нужно передать ее через провод) или просто сохранить ее как Unicode, если она останется на Python. Если вы хотите конвертировать в UTF-8, используйте функцию oppose, string.encode.

>>> uni.encode("utf-8")
'foo \xe2\x80\x9cbar bar \xe2\x80\x9d weasel'

Кудрявые кавычки занимают 3 байта для кодирования в UTF-8. Вы можете использовать UTF-16, и они будут только двумя байтами. Вы не можете кодировать ASCII или Latin-1, хотя они не имеют курсорных котировок.

Ответ 2

ИЗМЕНИТЬ. Похоже, ваша строка закодирована таким образом, что " (LEFT DOUBLE QUOTATION MARK) становится \x93, а " (ПРАВИЛЬНАЯ ДВОЙНАЯ ЦЕЛЕВОЙ МАРКИ) становится \x94. Существует множество кодовых страниц с таким отображением, CP1250 является одним из них, поэтому вы можете использовать это:

s = s.decode('cp1250')

Для всех кодовых страниц, которые отображают " в \x93, см. здесь (все они также отображают " в \x94, который можно проверить здесь).