Não é possível usar `cut -c` (` --characters`) com UTF-8?

15

O comando cuttem uma opção -cpara trabalhar em personagens, em vez de bytes com a opção -b. Mas isso não parece funcionar, no en_US.UTF-8local:

O segundo byte fornece o segundo caractere ASCII (que é codificado da mesma forma em UTF-8):

$ printf 'ABC' | cut -b 2          
B

mas não fornece o segundo dos três caracteres gregos não ASCII no código do idioma UTF-8:

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

Está tudo bem - é o segundo byte .
Então, olhamos para o segundo caractere :

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

Isso parece quebrado.
Com algumas experiências, verifica-se que o intervalo 3-4mostra o segundo caractere:

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

Mas isso é igual aos bytes 3 a 4:

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

Portanto, o valor -cnão é superior ao -bUTF-8.

Eu esperaria que a configuração de localidade não seja adequada para UTF-8, mas, em comparação, wcfunciona como esperado;
É frequentemente usado para contar bytes, com a opção -c( --bytes). (Observe os nomes das opções confusas.)

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

Mas também pode contar caracteres com a opção -m( --chars), que simplesmente funciona:

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

Portanto, minha configuração parece estar correta - mas algo é especial cut.

Talvez ele não suporte UTF-8? Mas parece suportar caracteres de vários bytes, caso contrário não precisaria suportar -be -c.

Então, oque há de errado? E porque?


A configuração da localidade parece correta para utf8, até onde eu sei:

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

A entrada, byte a byte:

$ printf 'αβγ' | hd 
00000000  ce b1 ce b2 ce b3                                 |......|
00000006
Volker Siegel
fonte
Interessante! Parece que -cestá usando o mesmo código que -b. Você deu uma olhada no código fonte? Talvez você possa encontrar uma dica para o que -crealmente se destina.
Michas

Respostas:

13

Você não disse o que cutestá usando, mas desde que você mencionou a opção GNU long, --charactersassumirei que é essa. Nesse caso, observe esta passagem deinfo coreutils 'cut invocation' :

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

Selecione para imprimir apenas os caracteres nas posições listadas na lista de caracteres. O mesmo que -bagora , mas a internacionalização vai mudar isso.

(enfase adicionada)

No momento, o GNU cutsempre funciona em termos de "caracteres" de byte único, portanto o comportamento que você vê é esperado.


O suporte às opções -be -cé requerido pelo POSIX - eles não foram adicionados ao GNU cutporque tinham suporte a vários bytes e funcionaram corretamente, mas para evitar erros na entrada compatível com POSIX. O mesmo -cfoi feito em algumas outras cutimplementações, embora não pelo menos no FreeBSD e no OS X.

Este é o comportamento histórico de -c. -bfoi adicionado recentemente para assumir a função de bytes, para que -cpossa funcionar com caracteres de vários bytes. Talvez em alguns anos funcione conforme desejado de maneira consistente, embora o progresso não tenha sido exatamente rápido (já faz mais de uma década). O GNU cut ainda nem implementa a -nopção , apesar de ortogonal e destinada a ajudar na transição. Existem possíveis problemas de compatibilidade com scripts antigos, o que pode ser uma preocupação, embora eu não saiba definitivamente qual é o motivo.

Michael Homer
fonte
1
bom trabalho. você encontrará o mesmo tipo de comentários nos trdocumentos do GNU também. e mesmo tarque eu não me lembre. Eu acho que é um grande projeto.
mikeserv
Existe alguma solução alternativa para o problema unicode em cut? Por exemplo, onde é possível baixar as fontes de patches cut? Ou seria mais fácil usar outro utilitário? (a grepsolução abaixo não funciona sem problemas com os intervalos, por exemplo 5-8,44-49)
dma_k 31/01
consulte este artigo de 2017, intitulado "Notas e indicadores aleatórios sobre o esforço contínuo para adicionar suporte a multibyte e unicode no GNU Coreutils" : crashcourse.housegordon.org/coreutils-multibyte-support.html
myrdd
você pode encontrar algumas alternativas cut -caqui: superuser.com/questions/506164/…
myrdd
5

colrm(parte de util-linux, já deve estar instalado na maioria das distribuições) parece lidar com a internacionalização muito melhor:

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

Cuidado com a numeração: colrm Nremoverá as colunas N, imprimindo caracteres até N-1.

( créditos )

Skippy le Grand Gourou
fonte
2

Como muitas grepimplementações têm reconhecimento multibyte, você também pode usar grep -opara simular alguns usos de cut -c.

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

Ajuste o número de períodos para simular cutintervalos.

Royce Williams
fonte