tr não substitui o apóstrofo [duplicado]

3

Eu quero converter todos os apóstrofos neste arquivo para X :

Bob's book
Bob’s book
Bob′s book  # (Might look the same but actually different)

O primeiro apóstrofo é substituído conforme esperado:

$ cat file | tr "'" "X"
BobXs book
Bob’s book
Bob′s book

Mas os dois outros tipos de apóstrofo, coisas estranhas acontecem:

$ cat file | tr "’" "X"
Bob's book
BobXXXs book
BobXX�s book

$ cat file | tr "′" "X"
Bob's book
BobXX�s book
BobXXXs book

Como fazer isso funcionar?

    
por Nicolas Raoul 19.09.2012 / 04:31

2 respostas

8

tr funciona em unidades de bytes, o que significa que não funciona corretamente para codificações de múltiplos bytes, como UTF-8. As únicas soluções que conheço são para encontrar uma versão de tr que suporte Unicode, ou mude para sed ou alguma outra ferramenta que possa fazer a substituição de strings.

    
por 19.09.2012 / 04:59
0

Para mim, tr funciona bem tanto para arquivos ascii quanto utf-8, desde que seu sistema operacional esteja configurado para trabalhar com a página de códigos utf-8.

Aqui está minha amostra # 1 (Solaris 11):

$ locale
LANG=en_US.UTF-8
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_ALL=

Como você pode ver, o sistema operacional está configurado para funcionar com o utf-8. Eu criei ambos os arquivos na página de códigos utf-8:

$ cat file
Bob’s Bob′s Bob's

$ cat apos
’′'

Então eu tenho resultados esperados substituindo todos apos assim:

$ cat file | tr "$(cat apos)" "xxx"
Bobxs Bobxs Bobxs

Aqui está minha amostra # 2 (Solaris 10):

$ locale
LANG=
LC_CTYPE="C"
LC_NUMERIC="C"
LC_TIME="C"
LC_COLLATE="C"
LC_MONETARY="C"
LC_MESSAGES="C"
LC_ALL=

Aqui você pode ver que este SO está configurado para manipular ASCII simples, não utf-8, então você pode esperar problemas no processamento de arquivos utf-8 com caracteres multi-byte usando tr. Mas há uma solução alternativa. Como o comando long tr permite inserir a representação octal do caractere, então você pode substituir todos os bytes do caractere especificado usando a representação octal.

No seu caso, você tem:

char  hex        octal
’     E2 80 99   201
′     E2 80 B2   202
'     27         

Firts e second apos são representados por três bytes. O terceiro é o padrão ascii (um byte).

Então, se você quiser substituir o primeiro apos você pode usar:

$ cat file | tr "201" "
$ cat file | tr "202" "
$ cat file | tr "" "x"
Bob’s Bob′s Bobxs
$ cat file | tr "2012" "
$ locale
LANG=en_US.UTF-8
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_ALL=
$ cat file
Bob’s Bob′s Bob's

$ cat apos
’′'
xxx" Bobxs Bobxs Bobxs
x" Bob▒s Bobxs Bob's
$ cat file | tr "$(cat apos)" "xxx"
Bobxs Bobxs Bobxs
x" Bobxs Bob▒s Bob's

Segundo:

$ locale
LANG=
LC_CTYPE="C"
LC_NUMERIC="C"
LC_TIME="C"
LC_COLLATE="C"
LC_MONETARY="C"
LC_MESSAGES="C"
LC_ALL=

Terceiro:

char  hex        octal
’     E2 80 99   201
′     E2 80 B2   202
'     27         

Para substituir tudo em um único tiro, você pode usar:

$ cat file | tr "201" "
$ cat file | tr "202" "
$ cat file | tr "" "x"
Bob’s Bob′s Bobxs
$ cat file | tr "2012" "%pre%%pre%xxx"
Bobxs Bobxs Bobxs
x" Bob▒s Bobxs Bob's
%pre%x" Bobxs Bob▒s Bob's

É claro que não é perfeito, desde que isso substitua todas as ocorrências de byte \ 342, \ 200, \ 231, \ 262 no arquivo, para que outros caracteres de múltiplos bytes que contenham esses bytes sejam quebrados. Mas, se o seu arquivo não contiver nenhum outro caractere multi-byte, ele funcionará.

    
por 09.05.2017 / 20:46