Создание символов новой строки UTF-16 в Python для Блокнота Windows

В Python 2.7, работающем в Ubuntu, этот код:

f = open("testfile.txt", "w")
f.write("Line one".encode("utf-16"))
f.write(u"\r\n".encode("utf-16"))
f.write("Line two".encode("utf-16"))

создает желаемую новую строку между двумя строками текста при чтении в Gedit:

Line one
Line two

Однако тот же самый код, выполненный в Windows 7 и прочитанный в Блокноте, производит неразборчивые символы после «Первой строки», но Блокнот не распознает новую строку. Как я могу написать правильные символы новой строки для UTF-16 в Windows, чтобы они соответствовали выводу, который я получаю в Ubuntu?

Я пишу вывод только для приложения Windows, которое читает только Unicode UTF-16. Я часами пробовал разные советы, но ничего не работает для Блокнота. Стоит отметить, что я могу успешно преобразовать текстовый файл в UTF-16 прямо в Блокноте, но я бы предпочел, чтобы скрипт изначально правильно сохранял кодировку.


person I_Ridanovic    schedule 18.06.2013    source источник
comment
В качестве примечания: "Line one".encode("utf-16") это довольно глупо. Обычно вы хотите вызывать encode только для строк Unicode, поэтому вы должны сделать u"Line one".encode("utf-16"). (3.x обеспечивает это, 2.7 - нет.) Я предполагаю, что в этом случае это просто артефакт вашего игрушечного примера, а не ваш реальный код.   -  person abarnert    schedule 18.06.2013
comment
PS, на будущее, вместо того, чтобы просто говорить непонятные символы, стоит на самом деле вставлять их сюда. Увидев, что строка 2 отображается как 䰀椀渀攀 琀眀漀, вы подтвердите, что у вас есть проблема с ошибкой на один байт, но не проблема с спецификацией, а ＀䳾椀渀攀 琀眀漀 покажет, что у вас есть обе проблемы и т. д.   -  person abarnert    schedule 18.06.2013
comment
Кроме того, это может быть не совсем непонятно. Кто-то, кто бегло знает китайский и обладает хорошим воображением, вероятно, мог бы интерпретировать это как что-то поэтическое о Бене, забирающемся в суповые тарелки времен династий Хань, Мин и Цин, и объяснить, что это вольный образный перевод второй строки. :)   -  person abarnert    schedule 18.06.2013


Ответы (1)


Проблема в том, что вы открываете файл в текстовом режиме, но пытаетесь использовать его как двоичный файл.

Этот:

u"\r\n".encode("utf-16")

… кодирует в '\r\0\n\0'.

Тогда это:

f.write('\r\0\n\0')

… преобразует новую строку Unix в новую строку Windows, давая '\r\0\r\n\0'.

И это, конечно, нарушает вашу кодировку UTF-16. Помимо того факта, что два байта \r\n будут декодированы в действующую, но не назначенную кодовую точку U+0A0D, это нечетное количество байтов, означающее, что у вас осталось \0. Таким образом, вместо L\0 следующим символом будет \0L, он же , и так далее.

Кроме того, вы, вероятно, пишете новую спецификацию UTF-16 для каждой закодированной строки. Большинство приложений Windows на самом деле прозрачно обрабатывают это и игнорируют их, поэтому все, что вы практически делаете, это тратите два байта на строку, но на самом деле это неправильно.


Быстрое решение первой проблемы — открыть файл в двоичном режиме:

f = open("testfile.txt", "wb")

Это не устраняет проблему с несколькими спецификациями, но устраняет неисправную проблему \n. Если вы хотите исправить проблему спецификации, вы либо используете кодирование с отслеживанием состояния, либо явно указываете 'utf-16-le' (или 'utf-16-be') для всех операций записи, кроме первой записи.


Но простое решение обеих проблем состоит в том, чтобы использовать модуль io (или модуль codecs для более ранних версий Python 2.x), чтобы выполнить всю тяжелую работу для ты:

f = io.open("testfile.txt", "w", encoding="utf-8")
f.write("Line one")
f.write(u"\r\n")
f.write("Line two")
person abarnert    schedule 18.06.2013
comment
Спасибо за подробное объяснение. В итоге я использовал решение с модулем кодеков. - person I_Ridanovic; 18.06.2013
comment
@I_Ridanovic: Если вам не нужна поддержка до версии 2.6, io.open обычно лучше, чем codecs.open. О некоторых способах см. issue 8796. (Кроме того, если вы используете codecs.open в Windows и уже испытываете проблемы с новой строкой, см. проблему 7262, чтобы узнать, как поведение отличается от документов.) - person abarnert; 18.06.2013