Как обрабатывать utf8 в командной строке (используя Perl или Python)?
Как я могу обработать utf8, используя Perl (или Python) в командной строке?
Например, я пытаюсь разделить символы в каждом слове. Это очень легко для текста не-utf8, например:
$ echo "abc def" | perl -ne 'my @letters = m/(.)/g; print "@letters\n"' | less
a b c d e f
Но с utf8 это не работает, конечно:
$ echo "одобрение за" | perl -ne 'my @letters = m/(.)/g; print "@letters\n"' | less
<D0> <BE> <D0> <B4> <D0> <BE> <D0> <B1> <D1> <80> <D0> <B5> <D0> <BD> <D0> <B8> <D0> <B5> <D0> <B7> <D0> <B0>
потому что он не знает о 2-байтовых символах.
Также было бы полезно узнать, как это делается (то есть обработка utf8 в командной строке) в Python.
5 ответов
Флаг "-C" управляет некоторыми функциями Perl Unicode (см. perldoc perlrun
):
$ echo "одобрение за" | perl -C -pe 's/.\K/ /g'
о д о б р е н и е з а
Чтобы указать кодировку, используемую для stdin/stdout, вы можете использовать PYTHONIOENCODING
переменная окружения:
$ echo "одобрение за" | PYTHONIOENCODING=utf-8 python -c'import sys
for line in sys.stdin:
print " ".join(line.decode(sys.stdin.encoding)),
'
о д о б р е н и е з а
Если вы хотите разбить текст на границы символов (графемы) (а не на кодовые точки, как в коде выше), вы можете использовать /\X/
регулярное выражение:
$ echo "одобрение за" | perl -C -pe 's/\X\K/ /g'
о д о б р е н и е з а
В питоне \X
поддерживается regex
модуль.
"Привет, - подумал я, - насколько сложно это может быть в Perl?"
Оказывается, это довольно легко. К сожалению, выяснить, как заняло у меня больше времени, чем я думал.
Быстрый взгляд на использование utf8 показал мне, что это уже устарело. Binmode Perl выглядел многообещающе, но не совсем.
Обнаружил, что есть Perluniintro, который привел меня к Perlunicode, который сказал, что я должен смотреть на Perlrun. Затем я нашел то, что искал.
Perl имеет переключатель командной строки -C
который переключает Perl в Unicode. Тем не менее -C
Переключатель командной строки также требует нескольких параметров. Вам нужно указать, что в юникоде. Есть удобная диаграмма, которая показывает различные варианты. Казалось бы, что perl -C
само по себе было бы хорошо. Это сочетает в себе различные варианты, что эквивалентно -CSDL
или же -C255
, Тем не менее, это означает, что если в вашем LOCALE не задано Unicode, Perl не будет работать в Unicode.
Вместо этого вы должны использовать perl -CSD
или же -perl -C63
,
$ echo "одобрение за" | perl -CSD -ne 'my @letters = m/(.)/g; print "@letters\n"'
о д о б р е н и е з а
Да, это работает.
Вы можете узнать совсем немного, просто ответив на вопрос.
$ echo "одобрение за" | python -c 'import sys, codecs ; x = codecs.
getreader("utf-8")(sys.stdin); print u", ".join(x.read().strip())'
о, д, о, б, р, е, н, и, е, , з, а
или если вы хотите кодовые точки Unicode:
$ echo "одобрение за" | python -c 'import sys, codecs ; x = codecs.
getreader("utf-8")(sys.stdin); print u", ".join("<%04x>" % ord(ch)
for ch in x.read().strip())'
<043e>, <0434>, <043e>, <0431>, <0440>, <0435>, <043d>, <0438>,
<0435>, <0020>, <0437>, <0430>
Я не знаю Perl, поэтому я отвечаю за Python.
Python не знает, что входной текст в Unicode. Вам нужно явно декодировать из UTF-8 или как там на самом деле, в Unicode. Затем вы можете использовать обычные средства обработки текста Python для его обработки.
http://docs.python.org/howto/unicode.html
Вот простая программа на Python 2.x, которую вы можете попробовать:
import sys
for line in sys.stdin:
u_line = unicode(line, encoding="utf-8")
for ch in u_line:
print ch, # print each character with a space after
Это копирует строки из стандартного ввода и преобразует каждую строку в Unicode. Кодировка указана как UTF-8. затем for ch in u_line
наборы ch
каждому персонажу. затем print ch,
это простой способ в Python 2.x напечатать символ с пробелом без возврата каретки. Наконец голый print
добавляет возврат каретки.
Я все еще использую Python 2.x для большей части моей работы, но для Unicode я бы порекомендовал вам использовать Python 3.x. Материал Unicode действительно улучшен.
Вот версия вышеуказанной программы на Python 3, протестированная на моем компьютере с Linux.
import sys
assert(sys.stdin.encoding == 'UTF-8')
for line in sys.stdin:
for ch in line:
print(ch, end=' ') # print each character with a space after
По умолчанию Python 3 предполагает, что вход кодируется как UTF-8. По умолчанию Python затем декодирует это в Unicode. Строки Python 3 всегда Unicode; есть особый тип bytes()
используется для строкового объекта, который содержит не-Unicode значения ("байты"). Это противоположность Python 2.x; в Python 2.x базовый тип строки представлял собой строку байтов, а строка Unicode была новой особой вещью.
Конечно, нет необходимости утверждать, что кодировка UTF-8, но это хороший простой способ документировать наши намерения и убедиться, что значение по умолчанию не изменилось.
В Python 3 print()
теперь функция. И вместо этого несколько странного синтаксиса добавления запятой после оператора print, чтобы заставить ее печатать пробел вместо новой строки, теперь есть аргумент именованного ключевого слова, который позволяет вам изменять конечный символ.
ПРИМЕЧАНИЕ: изначально у меня был голый print
оператор после обработки строки ввода в программе Python 2.x, и print()
в программе Python 3.x. Как отметил Дж.Ф. Себастьян, код печатает символы из строки ввода, а последний символ будет новой строкой, поэтому в дополнительном операторе печати нет необходимости.
Чтобы обрабатывать UTF-8 в командной строке с помощью Perl, мы должны учитывать STDIN, STDOUT, STDERR, аргументы и исходный код (данный в качестве аргумента для
-e
или вариант).
Рассмотрим следующий тестовый пример:
echo -n "одобрение за" | perl -Mstrict -w -E '
while (<STDIN>){ s/\X\K/ /g; say; }
say "Arguments and their length:";
say " $_\t", length("$_") foreach @ARGV;
say "Length of in the source code is ", length("");
' a
Это хороший тестовый пример, потому что он имеет символы в кодировке UTF-8 в 3-х местах:
- на стандартном вводе,
- в качестве аргументов и
- в исходном коде (предоставляется в качестве аргумента
-E
вариант).
(Кстати, мой терминал находится в локали UTF-8.)
Результат:
� � � � � � � � � � � � � � � � � � � � � �
Arguments and their length:
a 1
4
Length of in the source code is 4
Во-первых, давайте избавимся от вопросительных знаков. Сообщим Perl, что стандартные потоки — это символы в кодировке UTF-8. Для этого добавьте:
echo -n "одобрение за" | perl -Mstrict -w -CSD -E '
while (<STDIN>){ s/\X\K/ /g; say; }
say "Arguments and their length:";
say " $_\t", length("$_") foreach @ARGV;
say "Length of in the source code is ", length("");
' a
Примечание. Я мог бы просто использовать, потому что
-C
подразумевает
-CSDL
что в системе с локалью UTF-8 совпадает с , как описано в perlrun.
Результат:
о д о б р е н и е з а
Arguments and their length:
a 1
ð 4
Length of ð in the source code is 4
Хорошо, что избавились от вопросительных знаков.
Но теперь эмодзи в аргументах и в исходном коде перепутались.
Мы должны сообщить Perl, что наши аргументы - UTF-8. Мы делаем это, изменив на:
echo -n "одобрение за" | perl -Mstrict -w -CSDA -E '
while (<STDIN>){ s/\X\K/ /g; say; }
say "Arguments and their length:";
say " $_\t", length("$_") foreach @ARGV;
say "Length of in the source code is ", length("");
' a
Результат:
о д о б р е н и е з а
Arguments and their length:
a 1
1
Length of ð in the source code is 4
Хороший. Аргумент emoji фиксирован, и его длина составляет 1 символ, как и ожидалось.
Смайлики в исходном коде по-прежнему проблематичны.
Чтобы сообщить perl, что исходный код закодирован как UTF-8, добавьтек исходному коду или к параметрам командной строки:
echo -n "одобрение за" | perl -Mutf8 -Mstrict -w -CSDA -E '
while (<STDIN>){ s/\X\K/ /g; say; }
say "Arguments and their length:";
say " $_\t", length("$_") foreach @ARGV;
say "Length of in the source code is ", length("");
' a
Результат:
о д о б р е н и е з а
Arguments and their length:
a 1
1
Length of in the source code is 1
Хорошо, теперь мы получаем ожидаемый результат для символа эмодзи, находящегося в исходном коде.
Резюме:
- Добавлять
-CSD
чтобы сообщить Perl, что стандартные потоки имеют кодировку UTF-8. - Измените это на
-CSDA
для обработки аргументов в кодировке UTF-8. - Добавлять
use utf8;
в исходный код или добавить-Mutf8
к опциям, чтобы сообщить Perl, что исходный код имеет кодировку UTF-8.