Как обрабатывать 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>
потому что он не знает о двухбайтовых символах.
Было бы также полезно знать, как это делается (например, обработка utf8 в командной строке) на Python.
Ответы
Ответ 1
Флаг "-C" управляет некоторыми функциями Unicode Perl (см. 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'
о д о б р е н и е з а
См. Графитовые границы кластера
В Python \X
поддерживается regex
module.
Ответ 2
"Эй, - подумал я, - как трудно это быть в Perl?"
Оказывается, это довольно легко. К сожалению, выяснение того, как у меня заняло больше времени, чем я думал.
Быстрый взгляд на использование utf8 показал мне, что это устарело. Perl binmode выглядел многообещающим, но не совсем.
Найден там Perluniintro, который приведет меня к Perlunicode, в котором говорится, что я должен смотреть на Perlrun. Затем я нашел то, что искал.
Perl имеет переключатель командной строки -C
, который переключает Perl в Unicode. Однако для командной строки -C
требуется также несколько параметров. Вам нужно указать, что в Юникоде. Там удобный график, который показывает вам различные варианты. Похоже, что perl -C
сам по себе будет в порядке. Это объединяет различные опции, эквивалентные -CSDL
или -C255
. Однако это означает, что если ваш LOCALE не установлен в Юникод, Perl не будет работать в Юникоде.
Вместо этого вы должны использовать perl -CSD
или -perl -C63
.
$ echo "одобрение за" | perl -CSD -ne 'my @letters = m/(.)/g; print "@letters\n"'
о д о б р е н и е з а
Yup, это работает.
Вы можете узнать немного, просто отвечая на вопрос.
Ответ 3
Я не знаю Perl, поэтому я отвечаю за Python.
Python не знает, что входной текст находится в Юникоде. Вам нужно явно декодировать из 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()
теперь является функцией. И вместо этого довольно странного синтаксиса добавления запятой после оператора печати, чтобы заставить его печатать пробел вместо новой строки, теперь есть аргумент именованного ключевого слова, который позволяет вам изменить конец char.
ПРИМЕЧАНИЕ. Первоначально у меня был голой оператор print
после обработки строки ввода в программе Python 2.x и print()
в программе Python 3.x. Как отметил Ю.Ф. Себастьян, код печатает символы из строки ввода, а последний символ будет символом новой строки, поэтому нет необходимости в дополнительном заявлении печати.
Ответ 4
$ 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>