Създаване на UTF-16 знаци за нов ред в Python за Windows Notepad

В 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 и прочетен в Notepad, създава неразбираеми знаци след „Първи ред“, но нов ред не се разпознава от Notepad. Как мога да напиша правилни знаци за нов ред за UTF-16 в Windows, за да съответства на изхода, който получавам в Ubuntu?

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


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 да се показва като 䰀椀渀攀 琀眀漀 ще потвърди, че имате проблема с разместване по един байт, но не и проблема с BOM, докато ＀䳾椀渀攀 琀眀漀 ще покаже, че имате и двата проблема и т.н.   -  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 BOM за всеки кодиран низ. Повечето приложения на Windows всъщност прозрачно ще се справят с това и ще ги игнорират, така че всичко, което на практика правите, е да губите два байта/ред, но всъщност не е правилно.


Бързото решение на първия проблем е да отворите файла в двоичен режим:

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

Това не коригира проблема с множество BOM, но коригира счупения проблем \n. Ако искате да коригирате проблема със спецификацията на материалите, или използвате кодиране със състояние, или изрично указвате 'utf-16-le' (или 'utf-16-be') за всички записи, освен за първото записване.


Но лесното решение за и двата проблема е да се използва модулът io (или, за по-стар Python 2.x, модулът codecs), за да свършите цялата тежка работа за Вие:

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. Вижте проблем 8796 за някои от начините. (Също така, ако използвате codecs.open в Windows и вече имате проблеми с новия ред, вижте проблем 7262 за това как поведението се различава от документите.) - person abarnert; 18.06.2013