¿No puede usar `cut -c` (` --characters`) con UTF-8?

15

El comando cuttiene una opción -cpara trabajar en caracteres, en lugar de bytes con la opción -b. Pero eso no parece funcionar, en la en_US.UTF-8configuración regional:

El segundo byte proporciona el segundo carácter ASCII (que está codificado de la misma manera en UTF-8):

$ printf 'ABC' | cut -b 2          
B

pero no da el segundo de tres caracteres griegos no ASCII en la configuración regional UTF-8:

$ printf 'αβγ' | cut -b 2         
�

Está bien, es el segundo byte .
Entonces miramos el segundo personaje en su lugar:

$ printf 'αβγ' | cut -c 2 
�

Eso se ve roto.
Con algunos experimentos, resulta que el rango 3-4muestra el segundo personaje:

$ printf 'αβγ' | cut -c 3-4
β

Pero eso es lo mismo que los bytes 3 a 4:

$ printf 'αβγ' | cut -b 3-4
β

Entonces el -cno hace más que el -bUTF-8.

Esperaría que la configuración regional no sea adecuada para UTF-8, pero en comparación, wcfunciona como se esperaba;
A menudo se usa para contar bytes, con la opción -c( --bytes). (Tenga en cuenta los nombres confusos de las opciones).

$ printf 'αβγ' | wc -c
6

Pero también puede contar caracteres con la opción -m( --chars), que simplemente funciona:

$ printf 'αβγ' | wc -m
3

Así que mi configuración parece estar bien, pero hay algo especial en esto cut.

Tal vez no es compatible con UTF-8 en absoluto? Pero parece admitir caracteres de varios bytes, de lo contrario no necesitaría admitir -by -c.

¿Así que qué hay de malo? ¿Y por qué?


La configuración regional parece correcta para utf8, por lo que puedo decir:

$ locale
LANG=en_US.UTF-8
LANGUAGE=en_US
LC_CTYPE=en_US.UTF-8
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=

La entrada, byte por byte:

$ printf 'αβγ' | hd 
00000000  ce b1 ce b2 ce b3                                 |......|
00000006
Volker Siegel
fuente
¡Interesante! Parece que -cestá usando el mismo código que -b. ¿Le echó un vistazo al código fuente? Tal vez puedas encontrar una pista de lo -cque realmente está destinado.
michas

Respuestas:

13

No ha dicho cuál cutestá usando, pero como ha mencionado la opción larga GNU--characters , supondré que es esa. En ese caso, tenga en cuenta este pasaje deinfo coreutils 'cut invocation' :

‘-c character-list’
‘--characters=character-list’

Seleccione para imprimir solo los caracteres en las posiciones enumeradas en la lista de caracteres. Lo mismo que -bpor ahora , pero la internacionalización cambiará eso.

(énfasis añadido)

Por el momento, GNU cutsiempre funciona en términos de "caracteres" de un solo byte, por lo que se espera el comportamiento que ve.


Apoyando tanto los -by -clas opciones está requerido por POSIX - que no se han añadido a GNU cutporque tenía el apoyo de varios bytes y que funcionaba correctamente, pero para evitar errores en la entrada compatible con POSIX. Lo mismo -cse ha hecho en algunas otras cutimplementaciones, aunque no en FreeBSD y OS X al menos.

Este es el comportamiento histórico de -c. -bse agregó recientemente para asumir el rol de byte para que -cpueda funcionar con caracteres de varios bytes. Tal vez en unos pocos años funcione de la manera deseada, aunque el progreso no haya sido rápido (ya ha pasado más de una década). GNU cut ni siquiera implementa la -nopción aún, a pesar de que es ortogonal y está destinada a ayudar en la transición. Existen posibles problemas de compatibilidad con scripts antiguos, lo que puede ser un problema, aunque no sé definitivamente cuál es el motivo.

Michael Homer
fuente
1
buen trabajo. encontrará el mismo tipo de comentarios en los trdocumentos de GNU también. e incluso a tarmenos que lo recuerde mal. Supongo que es un gran proyecto.
mikeserv
¿Hay alguna solución para el problema Unicode cut? Por ejemplo, ¿dónde es posible descargar las fuentes para parches cut? ¿O sería más fácil usar otra utilidad? ( grepSolución por debajo de no funciona sin problemas con rangos por ejemplo 5-8,44-49)
dma_k
consulte este artículo de 2017, subtitulado "Notas aleatorias e indicadores sobre el esfuerzo continuo
myrdd
se pueden encontrar algunas alternativas a cut -caquí: superuser.com/questions/506164/...
myrdd
5

colrm(parte de util-linux, ya debería estar instalado en la mayoría de las distribuciones) parece manejar la internacionalización mucho mejor:

$ echo 'αβγ' | colrm 3
αβ
$ echo 'αβγ' | colrm 2
α

Tenga cuidado con la numeración: colrm Neliminará columnas de N, imprimiendo caracteres hasta N-1.

( créditos )

Skippy le Grand Gourou
fuente
2

Dado que muchas grepimplementaciones son compatibles con varios bytes, también puede usar grep -opara simular algunos usos de cut -c.

$ echo Τηεοδ29 | grep -o '^..'
Τη
$ echo Τηεοδ29 | egrep -o '^..' | grep -o '.$'
η

Ajuste el número de períodos para simular cutrangos.

Royce Williams
fuente