Файл UTF-16 потоковой обработки с окончаниями строк BOM и Unix в Perl для Windows

Мне нужно обработать с помощью perl текстовый файл размером 1 Гб, закодированный в кодировке UTF-16 с прямым порядком байтов, с окончаниями в стиле unix (т.е. только 0x000A без 0x000D в потоке) и LE BOM в начале. Файл обрабатывается в Windows (необходимы и Unix-решения). Под потоковым процессом я подразумеваю использование while (‹>), построчное чтение и запись. Было бы неплохо иметь однострочную командную строку, например:
perl -pe "BEGIN { SOME_PREPARATION }; s/SRC/DST/g;" infile.txt > outfile.txt

Шестнадцатеричный дамп ввода для тестирования (две строки: буквы "a" и "b" в каждой): FF FE 61 00 0A 00 62 00 0A 00

обработка типа s/b/c/g должна дать результат ("b" заменено на "c"): FF FE 61 00 0A 00 63 00 0A 00

PS. Прямо сейчас со всеми моими испытаниями либо есть проблема с выводом CRLF (байты 0D 0A выводятся, создавая неверный символ юникода, и мне нужно только 0A00 без 0D00, чтобы сохранить тот же стиль unix), либо каждая новая строка переключает LE/BE, т.е. " на одной строке 6100 на нечетных строках и 0061 на четных строках в выводе.


person Arty    schedule 25.02.2012    source источник
comment
Перл 5.что? Есть некоторые различия, и я хочу убедиться, что мой ответ работает для вас.   -  person ikegami    schedule 26.02.2012
comment
Причина сложности в том, что Perl добавляет :crlf до того, как у вас есть возможность добавить :encoding(UTF-16le) в дескриптор, и это ставит их в неправильный порядок.   -  person ikegami    schedule 26.02.2012
comment
@ikegami, можете ли вы объяснить использование :crlf? Что он делает, если присутствует как :crlf:encoding(UTF-16LE)?   -  person Arty    schedule 26.02.2012


Ответы (1)


Лучшее, что я придумал, это:

perl -pe "BEGIN { binmode $_, ':raw:encoding(UTF-16LE)' for *STDIN, *STDOUT }; s/b/c/g;" <infile.txt >outfile.txt

Но обратите внимание, что мне пришлось использовать <infile.txt вместо infile.txt, чтобы файл находился в STDIN. Теоретически прагма open должна управлять кодировкой, используемой волшебным дескриптором файла ARGV, но я не могу заставить его работать правильно в этом случае.

Разница между <infile.txt и infile.txt заключается в том, как и когда открываются файлы. С помощью <infile.txt файл подключается к стандартному вводу и открывается перед запуском Perl. Когда вы binmode STDIN в блоке BEGIN файл уже открыт, и вы можете изменить кодировку.

При использовании infile.txt имя файла передается в качестве аргумента командной строки и помещается в массив @ARGV. Когда блок BEGIN выполняется, файл еще не открыт, поэтому вы не можете установить его кодировку. Теоретически вы должны быть в состоянии сказать:

use open qw(:std IO :raw:encoding(UTF-16LE));

и пусть волшебная обработка <ARGV> применит правильную кодировку. Но я не смог заставить это работать правильно в этом случае.

person cjm    schedule 25.02.2012
comment
Ух ты! Прекрасно работает! :) Спасибо @cjm. Как я выяснил, экспериментируя прямо сейчас, основной проблемой проблем в моем коде было отсутствие ‹ перед infile.txt. Можете ли вы объяснить, почему это необходимо и какая разница? Если не STDIN, то куда файл перенаправляется без ‹? Потому что perl -pe print file.txt работает так же, как если бы file.txt находится в STDIN внутри скрипта... Кстати, в чем разница между использованием UTF-16LE и UTF-16 здесь, в скрипте? - person Arty; 26.02.2012
comment
Почему :raw? Кроме того, я use open все время с :utf8 и магией ARGV. - person tchrist; 26.02.2012
comment
@tchrist, я использовал :raw, потому что он работает в Windows, где Perl по умолчанию добавляет :crlf, но он хочет, чтобы вывод был с окончаниями строк Unix. Я не знаю лучшего способа удалить :crlf. - person cjm; 26.02.2012