tr analog para caracteres unicode?

6

Eu preciso de um utilitário internacionalizado que faça a mesma coisa que tr : obtém o caractere do fluxo e o substitui por um caractere correspondente. Não é uma solução de caso particular, como de baixo para cima, mas uma solução de caso geral é necessária. Sem gorillion canalizado sed chamadas, se possível.

Observe que tr não funciona no Linux: ele traduz bytes, não caracteres. Isso falha com codificações multibyte.

$ tr --version | head -n 1
tr (GNU coreutils) 8.23
$ echo $LC_CTYPE
en_US.UTF-8
$ echo 'Ångstrom' | tr Æ Œ         
Ņngstrom
    
por fedoraman 31.08.2017 / 22:45

3 respostas

9

O GNU sed funciona com caracteres de múltiplos bytes. Então:

$ echo é½Æ | sed 'y/é½Æ/ABŒ/'
ABŒ

Não é tanto que o GNU tr não tenha sido internacionalizado, mas que ele não suporta caracteres de múltiplos bytes (como os não-ASCII em locais UTF-8). O GNU tr funcionaria com Æ , Œ desde que fossem de byte único, como no conjunto de caracteres iso8859-15.

Mais sobre isso em Como tornar o conhecimento de caracteres não-ascii (unicode)?

Em qualquer caso, isso não tem nada a ver com o Linux, é sobre a implementação tr no sistema. Se esse sistema usa o Linux como um kernel ou o tr é construído para o Linux ou usa a API do kernel do Linux, isso não é relevante, já que a parte da funcionalidade tr ocorre no espaço do usuário.

busybox tr e GNU tr são os mais comumente encontrados em distribuições de software criados para Linux e não suportam caracteres de múltiplos bytes, mas há outros que foram portados para o Linux como tr de o toolchest da herança (portado do OpenSolaris) ou do ast-open que faz.

Observe que sed do y não suporta intervalos como a-z . Observe também que, se esse script que contiver sed 'y/é½Æ/ABŒ/' for gravado no conjunto de caracteres UTF-8, ele não funcionará mais como esperado se for chamado em uma localidade onde UTF-8 não é o conjunto de caracteres.

Uma alternativa poderia ser usar perl :

perl -Mopen=locale -Mutf8 -pe 'y/a-zé½Æ/A-ZABŒ/'

Acima, espera-se que o código perl esteja em UTF-8, mas processará a entrada na codificação da localidade (e na mesma codificação). Se chamado em um código do idioma UTF-8, ele transliterará um UTF-8 Æ (0xc3 0x86) para um UTF-8 Œ (0xc5 0x92) e em um ISO8859-15, mas para 0xc6 - > 0xbc.

Na maioria dos shells, ter esses caracteres UTF-8 dentro das aspas simples deve ser OK mesmo que o script seja chamado em uma localidade onde UTF-8 não seja o charset (uma exceção é yash que reclamaria se esses bytes não formam caracteres válidos no idioma). Se você estiver usando outras citações do que aspas simples, isso pode causar problemas. Por exemplo,

perl -Mopen=locale -Mutf8 -pe "y/♣\'/&'/"

falharia em uma localidade onde o conjunto de caracteres é BIG5-HKSCS porque a codificação de \ (0x5c) também está contida em alguns outros caracteres (como α : 0xa3 0x5c e a codificação UTF-8 de acaba em 0xa3).

De qualquer forma, não espere coisas como

perl -Mopen=locale -Mutf8 -pe 'y/Á-Ź/A-Z/'

para trabalhar na remoção de acentos agudos. O acima é na verdade apenas

perl -Mopen=locale -Mutf8 -pe 'y/\x{c1}-\x{179}/\x{41}-\x{5a}/'

Ou seja, o intervalo é baseado nos pontos de código unicode. Portanto, os intervalos não serão úteis fora de sequências muito bem definidas que estão na ordem " correto " em Unicode como A-Z , 0-9 .

Se você quiser remover os acentos agudos, precisará usar ferramentas mais avançadas, como:

perl -Mopen=locale -MUnicode::Normalize -pe '
  $_ = NFKD($_); s/\x{301}//g; $_ = NFKC($_)'

Isso é usar formulários de normalização Unicode para decompor caracteres, remover os acentos agudos (aqui o formulário combinando U+0301 ) e recompor.

Outra ferramenta útil para transliterar Unicode é uconv de ICU . Por exemplo, o acima também pode ser escrito como:

uconv -x '::NFKD; \u0301>; ::NFKC;'

Embora funcione apenas em dados UTF-8. Você precisaria:

iconv -t utf-8 | uconv -x '::NFKD; \u0301>; ::NFKC;' | iconv -f utf-8

Para processar dados na localidade do usuário.

    
por 01.09.2017 / 00:42
1

No Bash, você pode usar a expansão de parâmetros .

Substituindo Å com êxito:

$ string='Hello Ångstrom'
$ a='Å'
$ b='Œ'
$ printf '%s\n' "${string//${a}/${b}}"
Hello Œngstrom

Tentando substituir Æ , o que não faz parte da sequência:

$ string='Hello Ångstrom'
$ a='Æ'
$ b='Œ'
$ printf '%s\n' "${string//${a}/${b}}"
Hello Ångstrom
    
por 01.09.2017 / 07:16
0

Pode ser o seu esquema de codificação. Tente executá-lo através de iconv assim:

echo Ångstrom | iconv -f UTF-8 | tr 'Å' 'Œ'

Sai com: Œngstrom

    
por 05.12.2017 / 21:50