2012-03-16 2 views
14

Как я могу обрабатывать 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.

+0

'$ sed 's /./&/g' <<<" одобрение за "' 'о д о б р е н и е з а' –

+1

@ Игнасио Васкес-Абрамс: 'sed 's /./&/g'' не работает для графемов (имеет значение, если текст содержит комбинированные символы, например, «Солженицын»). В Perl Python можно решить с помощью '/ \ X /' regex. – jfs

ответ

24

"-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' 
о д о б р е н и е з а 

Grapheme Cluster Boundaries См

В Python \X поддерживается regex module.

+1

+1 для получения «Солженицын», чтобы вести себя! – DSM

+0

Прохладный, но что такое '\ K'? – Frank

+0

@Frank: ['\ K хранит материал слева от него] (http://perldoc.perl.org/perlre.html# (% 3f% 3c% 3dpattern) -% 5cK) – jfs

4

Я не знаю 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() теперь функция. И вместо этого довольно странного синтаксиса добавления запятой после оператора печати, чтобы заставить его печатать пробел вместо новой строки, теперь есть аргумент именованного ключевого слова, который позволяет вам изменить символ конца.

ПРИМЕЧАНИЕ: Первоначально у меня был голый оператор print после обработки входной строки в программе Python 2.x и print() в программе Python 3.x. Как отметил J.F.Sebastian, код печатает символы из строки ввода, а последний символ будет символом новой строки, поэтому на самом деле нет необходимости в дополнительной заявке на печать.

+0

Python 3.x unicode действительно сильно не изменился. Изменены только кодировка по умолчанию и литералы в коде. Также некоторые вещи были переименованы. Никаких новых функций в этом отношении не было добавлено. – nosklo

+0

@nosklo, как показывает мой второй пример, значения по умолчанию теперь распознаются Unicode в Python 3.x. Нет необходимости явно преобразовывать входную строку в строку Unicode; вы можете просто обработать его. Это очень важное изменение IMHO. – steveha

+0

уже есть новая строка; вам не нужен годовой оператор 'print', т. е.' print '\ n ",' печатает новую строку самостоятельно. – jfs

4
$ echo "одобрение за" | python -c 'import sys, codecs ; x = codecs. 
getreader("utf-8")(sys.stdin); print u", ".join(x.read().strip())' 
о, д, о, б, р, е, н, и, е, , з, а 

или если вы хотите Юникода: кодовые

$ 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> 
+1

это не будет работать, если stdout перенаправляется, например, 'python -c ... | cat' – jfs

5

«Эй», подумал я, «как трудно это могло быть в Perl?»

Оказывается, это довольно легко. К сожалению, выяснение того, как у меня заняло больше времени, чем я думал.

Быстрый взгляд на use utf8 показал мне, что это устарело. Perl's binmode выглядел многообещающим, но не совсем.

Найдено Perluniintro, которые ведут меня к Perlunicode, который сказал, что я должен посмотреть на Perlrun. Затем я нашел то, что искал.

У Perl есть переключатель командной строки -C, который переключает Perl в Unicode. Однако переключатель командной строки -C также требует нескольких параметров. Вам нужно указать, что находится в Юникоде. Там есть convenient chart, который показывает вам различные варианты. Похоже, что perl -C сам по себе будет в порядке. Это объединяет различные варианты, которые эквивалентны -CSDL или -C255. Однако это означает, что если ваш LOCALE не установлен в Юникод, Perl не будет работать в Юникоде.

Вместо этого вы должны использовать perl -CSD или -perl -C63.

$ echo "одобрение за" | perl -CSD -ne 'my @letters = m/(.)/g; print "@letters\n"' 
о д о б р е н и е з а 

Yup, это работает.

Вы можете узнать совсем немного, просто отвечая на вопрос.

+1

+1: вы могли бы означать '-CSDA' (для обработки' @ ARGV'), хотя из OP локаль можно считать 'utf-8', поэтому достаточно просто -C'. – jfs

+2

Использование utf8 не совсем устарело, просто у него есть только ограниченная цель сообщить perl, что ваш исходный код находится в utf8. Вам нужно делать другие вещи для глотания и извлечения данных в utf8. – Alex

+3

Ну, прагма utf8 началась гораздо более амбициозно, чем в итоге. Он был задуман как нечто, что было бы больше похоже на utf8 :: all. –

Смежные вопросы