2012-03-16 19 views
14

¿Cómo puedo manejar utf8 usando Perl (o Python) en la línea de comando?¿Cómo manejar utf8 en la línea de comando (usando Perl o Python)?

Estoy tratando de dividir los caracteres en cada palabra, por ejemplo. Esto es muy fácil para el texto no UTF-8, por ejemplo:

$ echo "abc def" | perl -ne 'my @letters = m/(.)/g; print "@letters\n"' | less 
a b c d e f 

Pero con UTF-8 no funciona, por supuesto:

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

, ya que no sabe nada de la de 2 bytes caracteres.

También sería bueno saber cómo se hace esto (es decir, el procesamiento de línea de comandos de utf8) en Python.

+0

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

+1

@Ignacio Vazquez-Abrams $: 'sed 's /./&/g'' no funciona para grafemas (es importante si un texto contiene caracteres combinados, por ejemplo, '" Солженицын "'). En Perl, Python se puede resolver usando '/ \ X /' regex. – jfs

Respuesta

24

La bandera "-C" controla algunas de las características de Perl Unicode (ver perldoc perlrun):

$ echo "одобрение за" | perl -C -pe 's/.\K/ /g' 
о д о б р е н и е з а 

Para especificar la codificación utilizada para stdin/stdout puede usar la variable de entorno PYTHONIOENCODING:

$ echo "одобрение за" | PYTHONIOENCODING=utf-8 python -c'import sys 
for line in sys.stdin: 
    print " ".join(line.decode(sys.stdin.encoding)), 
' 
о д о б р е н и е з а 

Si desea dividir el texto en caracteres (grafemas) límites (no en los puntos de código como el código anterior), entonces usted podría utilizar /\X/ expresión regular:

$ echo "одобрение за" | perl -C -pe 's/\X\K/ /g' 
о д о б р е н и е з а 

Ver Grapheme Cluster Boundaries

En Python \X es compatible con regex module.

+1

+1 para obtener "Солженицын" para comportarse! – DSM

+0

Genial, pero ¿qué es '\ K'? – Frank

+0

@Frank: ['\ K mantiene las cosas que quedan de él'] (http://perldoc.perl.org/perlre.html# (% 3f% 3c% 3dpattern) -% 5cK) – jfs

4

No sé Perl, entonces estoy respondiendo por Python.

Python no sabe que el texto de entrada está en Unicode. Necesita decodificar explícitamente desde UTF-8 o lo que sea que realmente sea, a Unicode. Entonces puede usar cosas normales de procesamiento de texto Python para procesarlo.

http://docs.python.org/howto/unicode.html

Aquí es un simple programa de Python 2.x para que usted intente:

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 

Esto copia líneas de la entrada estándar, y convierte cada línea a Unicode. La codificación se especifica como UTF-8. Luego for ch in u_line establece ch para cada personaje. Luego, print ch, es la manera más fácil en Python 2.x para imprimir un personaje, seguido de un espacio, sin retorno de carro. Finalmente, un print desnudo agrega un retorno de carro.

Todavía uso Python 2.x para la mayoría de mi trabajo, pero para Unicode recomendaría que use Python 3.x. Las cosas Unicode realmente han mejorado.

Aquí está la versión de Python 3 del programa anterior, probado en mi computadora con 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 

De manera predeterminada, Python 3 supone que la entrada está codificada como UTF-8. Por defecto, Python luego decodifica eso en Unicode. Las cadenas de Python 3 son siempre Unicode; hay un tipo especial bytes() usado para un objeto tipo cadena que contiene valores no Unicode ("bytes"). Esto es lo opuesto a Python 2.x; en Python 2.x, el tipo de cadena básica era una cadena de bytes, y una cadena Unicode era algo nuevo especial.

Por supuesto, no es necesario afirmar que la codificación es UTF-8, pero es una forma sencilla y agradable de documentar nuestras intenciones y asegurarse de que el valor predeterminado no se modifique de alguna manera.

En Python 3, print() ahora es una función. Y en lugar de la extraña sintaxis de agregar una coma después de una declaración de impresión para imprimir un espacio en lugar de una línea nueva, ahora hay un argumento de palabra clave con nombre que le permite cambiar el carácter final.

NOTA: Originalmente tenía una declaración print desnuda después de manejar la línea de entrada en el programa Python 2.x, y print() en el programa Python 3.x. Como señaló J.F. Sebastian, el código está imprimiendo caracteres desde la línea de entrada, y el último carácter será una nueva línea, por lo que realmente no es necesario el enunciado de impresión adicional.

+0

Las cosas de Unicode en Python 3.x realmente no cambiaron mucho. Solo la codificación predeterminada y los literales en el código en sí han cambiado. También algunas cosas han sido renombradas. No se ha agregado ninguna funcionalidad nueva a este respecto. – nosklo

+0

@nosklo, como muestra mi segundo ejemplo, los valores predeterminados ahora son compatibles con Unicode en Python 3.x. No es necesario convertir explícitamente la cadena de entrada a una cadena Unicode; solo puedes procesarlo. Ese es un cambio bastante importante en mi humilde opinión. – steveha

+0

ya hay una nueva línea; no necesita una declaración 'print' desnuda, es decir, 'print' \ n '',' imprime la nueva línea por sí mismo. – jfs

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

o si quieres puntos de código 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> 
+1

esto no funcionará si stdout se redirige, por ejemplo, 'python -c ... | cat' – jfs

5

"Hola", pensé, "¿qué tan difícil podría ser esto en Perl?"

Resulta que es bastante fácil. Lamentablemente, descubrir cómo me llevó más tiempo de lo que pensaba.

Un vistazo rápido a use utf8 me mostró que esto ya está obsoleto. El binmode de Perl parecía prometedor, pero no del todo.

Encontrado hay Perluniintro que me llevan a Perlunicode que dijo que debería mirar Perlrun. Entonces, encontré lo que estaba buscando.

Perl tiene un interruptor de línea de comando -C que conmuta Perl a Unicode. Sin embargo, el cambio de línea de comando -C también requiere algunas opciones. Debe especificar qué hay en Unicode. Hay un convenient chart que le muestra las diversas opciones. Parece que perl -C por sí solo estaría bien. Esto combina varias opciones que es equivalente a -CSDL o -C255. Sin embargo, eso significa que si su LOCALE no está configurado como unicode, Perl no funcionará en Unicode.

En su lugar, debe utilizar perl -CSD o -perl -C63.

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

Sí, eso funciona.

Puede aprender bastante solo respondiendo una pregunta.

+1

+1: puede querer decir '-CSDA' (para procesar' @ ARGV'), aunque desde el OP se puede suponer que la configuración regional está basada en 'utf-8', por lo que basta con '-C -C'. – jfs

+2

use utf8 no es exactamente obsoleto, es solo que tiene el propósito limitado de decirle a Perl que su código fuente está en utf8. Necesita hacer otras cosas para ingerir y expulsar datos en utf8. – Alex

+3

Bueno, el pragma utf8 comenzó mucho más ambicioso de lo que terminó. Fue concebido como algo que sería más parecido a utf8 :: todo. –

Cuestiones relacionadas