Написание юникода с помощью python — что не так с этим символом

С python 2.7 я читаю как unicode и пишу как utf-16-le. Большинство символов интерпретируются правильно. Но некоторые из них не являются, например, u'脊', также известный как unichr(33034). Следующий код кода пишется неправильно:

import codecs
with open('temp.txt','w') as temp:
    temp.write(codecs.BOM_UTF16_LE)     
    text = unichr(33034)  # text = u'\u810a'
    temp.write(text.encode('utf-16-le'))

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

  1. unichr(33033) и unichr(33035) работают правильно.

  2. кодировка utf-8 (без спецификации, знак порядка следования байтов).

Как я могу распознавать символы, которые не будут записываться правильно, и как я могу написать файл с кодировкой utf-16-le со спецификацией, которая либо печатает эти символы, либо какую-то замену?


person philshem    schedule 16.09.2013    source источник
comment
Пожалуйста, определите неправильно. Что вы ожидали? Что происходит вместо этого?   -  person Pavel Anossov    schedule 16.09.2013
comment
При использовании unichr(33033) и unichr(33035) выводом является правильный символ хань. Но когда я пишу unichr(33034), пытаясь написать 脊, я получаю искаженный текст.   -  person philshem    schedule 16.09.2013
comment
Что вы используете для просмотра файла? Какие байты записываются и какие байты вы ожидали?   -  person Wooble    schedule 16.09.2013
comment
Я использую блокнот ++, который правильно отображает символ при вставке. При записи символа с помощью вышеуказанного метода шестнадцатеричные значения не совпадают.   -  person philshem    schedule 16.09.2013


Ответы (3)


Вы открываете файл в текстовом режиме, что означает, что символы/байты разрыва строки будут быть переведены на местную конвенцию. К сожалению, символ, который вы пытаетесь записать, содержит байт 0A, который интерпретируется как разрыв строки и не попадает в файл правильно.

Вместо этого откройте файл в двоичном режиме:

open('temp.txt','wb')
person Joni    schedule 16.09.2013
comment
Этот ответ исправляет проблему. Большое спасибо! Я приму, как только полностью отлажу. - person philshem; 16.09.2013
comment
Ясно и лаконично - еще раз спасибо! - person philshem; 16.09.2013

Ответ @Joni является корнем проблемы, но если вместо этого вы используете codecs.open, он всегда открывается в двоичном режиме, даже если он не указан. Использование кодека utf16 также автоматически записывает спецификацию с использованием собственного порядка байтов:

import codecs
with codecs.open('temp.txt','w','utf16') as temp:
    temp.write(u'\u810a')

Шестнадцатеричный дамп temp.txt:

FF FE 0A 81

Ссылка: codecs.open

person Mark Tolonen    schedule 17.09.2013

Вы уже используете библиотеку кодеков. При работе с этим файлом вы должны поменять местами использование open() с codecs.open() для прозрачной обработки кодирования.

import codecs
with codecs.open('temp.txt', 'w', encoding='utf-16-le') as temp:
    temp.write(unichr(33033))
    temp.write(unichr(33034))
    temp.write(unichr(33035))

Если после этого у вас возникнут проблемы, возможно, проблема связана с вашим средством просмотра, а не со скриптом Python.

person Jordan    schedule 16.09.2013
comment
Выглядит хорошо, но если я возьму ваш код (и добавлю as temp в открытую строку), я получу следующую ошибку: UnicodeDecodeError: кодек ascii не может декодировать байт 0xff в позиции 0: порядковый номер не в диапазоне (128) . - person philshem; 16.09.2013
comment
Спасибо за исправление. Я заметил, что использовал символы подчеркивания вместо тире для кодировки. Пожалуйста, попробуйте еще раз. - person Jordan; 16.09.2013
comment
Вы не должны явно писать спецификацию, просто откройте файл с encoding='utf-16', и спецификация будет написана для вас. См. этот ответ для объяснения stackoverflow.com/a/5726295/107660 - person Duncan; 16.09.2013