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

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

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

$ echo "abc def" | perl -ne 'my @letters = m/(.)/g; print "@letters\n"' | less abcdef 

Pero con utf8 no funciona, por supuesto:

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

porque no sabe acerca de los caracteres de 2 bytes.

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

El indicador “-C” controla algunas de las funciones de Perl Unicode (ver perldoc perlrun ):

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

Para especificar la encoding 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 los límites de los caracteres (grafema) (no en los puntos de código como en el código anterior), puede usar /\X/ expresión regular:

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

Ver Grapheme Cluster Límite

En Python \X es compatible con el módulo regex .

“Oye”, pensé, “¿qué tan difícil podría ser esto en Perl?”

Resulta que es bastante fácil. Desafortunadamente, descubrir cómo me llevó más tiempo del que pensaba.

Un rápido vistazo al uso de utf8 me mostró que ahora está obsoleto. El modo de bin de Perl parecía prometedor, pero no del todo.

Encontré que hay un Perluniintro que me llevó a Perlunicode que decía que debería mirar a Perlrun . Entonces, encontré lo que estaba buscando.

Perl tiene un interruptor de línea de comando -C que cambia Perl a Unicode. Sin embargo, el -C línea de comando -C también requiere algunas opciones. Necesitas especificar que hay en Unicode. Hay una tabla conveniente que 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 en 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.

Puedes aprender bastante solo respondiendo una pregunta.

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

No conozco a Perl, así que respondo por Python.

Python no sabe que el texto de entrada está en Unicode. Debe decodificar explícitamente de UTF-8 o lo que sea que realmente sea, a Unicode. Luego puedes usar el procesamiento normal de texto de Python para procesarlo.

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

Aquí hay un progtwig Python 2.x simple para que pruebes:

 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 las líneas de la entrada estándar y convierte cada línea a Unicode. La encoding se especifica como UTF-8. Entonces for ch in u_line establece ch para cada carácter. Luego, print ch, es la forma fácil en Python 2.x para imprimir un carácter, seguido de un espacio, sin retorno de carro. Por último, una print desnuda añade un retorno de carro.

Sigo usando Python 2.x para la mayor parte de mi trabajo, pero para Unicode, le recomendaría que use Python 3.x. Las cosas de Unicode están realmente mejoradas.

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

Por defecto, Python 3 asume que la entrada está codificada como UTF-8. Por defecto, Python luego lo decodifica en Unicode. Python 3 cadenas son siempre Unicode; hay un tipo especial de bytes() utilizado para un objeto similar a una cadena que contiene valores que no son Unicode (“bytes”). Esto es lo contrario de Python 2.x; en Python 2.x, el tipo de cadena básico era una cadena de bytes, y una cadena Unicode era una cosa especial nueva.

Por supuesto, no es necesario afirmar que la encoding es UTF-8, pero es una buena forma sencilla de documentar nuestras intenciones y asegurarse de que el valor predeterminado no se haya modificado de alguna manera.

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

NOTA: Originalmente tuve una statement de print simple después de manejar la línea de entrada en el progtwig Python 2.x e print() en el progtwig Python 3.x. Como señaló JF 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 hay necesidad de la statement de impresión adicional.