Eu tenho um arquivo de texto em uma codificação desconhecida ou mista. Quero ver as linhas que contêm uma sequência de bytes que não é UTF-8 válida (canalizando o arquivo de texto em algum programa). Equivalentemente, quero filtrar as linhas que são válidas UTF-8. Em outras palavras, estou procurando .grep [notutf8]
Uma solução ideal seria portátil, curta e generalizável para outras codificações, mas se você achar que a melhor maneira é assar na definição de UTF-8 , vá em frente.
command-line
text-processing
character-encoding
unicode
Gilles 'SO- parar de ser mau'
fonte
fonte
Respostas:
Se você deseja usar
grep
, você pode:nos locais UTF-8 para obter as linhas que tenham pelo menos uma sequência UTF-8 inválida (isso funciona pelo menos com o GNU Grep).
fonte
-a
, isso é necessário para trabalhar com o POSIX. No entanto, o GNUgrep
pelo menos falha em localizar os não caracteres ou pontos de código substitutos UTF-16 codificados em UTF-8 acima de 0x10FFFF.-a
GNU é necessáriogrep
(que não é compatível com POSIX, suponho). Em relação à área substituta e aos pontos de código acima de 0x10FFFF, isso é um bug da época (o que poderia explicar isso ). Para isso, a adição-P
deve funcionar com o GNUgrep
2.21 (mas é lento); é um bug pelo menos no Debian grep / 2.20-4 .grep
é um utilitário de texto (espera-se que funcione apenas na entrada de texto), então suponho que o comportamento do GNU grep seja tão válido quanto qualquer outro aqui.grep
(cuja intenção é considerar sequências inválidas como não correspondentes) e possíveis erros.Eu acho que você provavelmente quer iconv . É para converter entre conjuntos de códigos e suporta um número absurdo de formatos. Por exemplo, para remover qualquer coisa inválida no UTF-8, você pode usar:
iconv -c -t UTF-8 < input.txt > output.txt
Sem a opção -c, ele reportará problemas na conversão para stderr; portanto, com a direção do processo, você pode salvar uma lista deles. Outra maneira seria retirar o material não-UTF8 e depois
diff input.txt output.txt
para obter uma lista de onde as alterações foram feitas.
fonte
iconv -c -t UTF-8 <input.txt | diff input.txt - | sed -ne 's/^< //p'
. Porém, ele não funcionará como um pipeline, já que você precisa ler a entrada duas vezes (não,tee
não funciona, pode bloquear dependendo de quanto buffericonv
ediff
fazer).diff <(iconv -c -t UTF-8 <input.txt) input.txt
Edit: Eu corrigi um erro de digitação no regex. Ele precisava de um '\ x80` e não \ 80 .
O regex para filtrar formulários UTF-8 inválidos, para aderência estrita a UTF-8, é o seguinte
Saída (das linhas principais. Do teste 1 ):
Q. Como alguém cria dados de teste para testar uma regex que filtra Unicode inválido?
A. Crie seu próprio algoritmo de teste UTF-8 e quebre suas regras ...
Catch-22 .. Mas então, como você testa seu algoritmo de teste?
O regex, acima, foi testado (usando
iconv
como referência) para cada valor inteiro de0x00000
até0x10FFFF
.. Esse valor superior é o valor inteiro máximo de um ponto de código UnicodeDe acordo com esta página UTF-8 da wikipedia .
Esse número (1.112.064) equivale a um intervalo
0x000000
para0x10F7FF
, que é 0x0800 tímido do valor inteiro máximo real para o ponto de código Unicode mais alto:0x10FFFF
Esse bloco de números inteiros está ausente no espectro Unicode Codepoints, devido à necessidade da codificação UTF-16 ir além de sua intenção de design original por meio de um sistema chamado pares substitutos . Um bloco de
0x0800
números inteiros foi reservado para ser usado pelo UTF-16. Esse bloco abrange o intervalo0x00D800
para0x00DFFF
. Nenhum desses intérpretes são valores Unicode legais e, portanto, são valores UTF-8 inválidos.No Teste 1 ,
regex
ele foi testado contra todos os números no intervalo de pontos de código Unicode e corresponde exatamente aos resultados deiconv
.. ie. Valores válidos de 0x010F7FF e valores inválidos de 0x000800 .No entanto, agora surge o problema de: * Como o regex lida com o valor UTF-8 fora da faixa; acima
0x010FFFF
(UTF-8 pode se estender para 6 bytes, com um valor inteiro máximo de 0x7FFFFFFF ?Para gerar os necessários * valores de bytes UTF-8 não unicode necessários , usei o seguinte comando:
Para testar sua validade (de alguma forma), usei o
Gilles'
regex UTF-8 ...A saída do 'do Perl chr print' corresponde a filtragem de regex Gilles' .. Um reforça a validade das outras .. Eu não posso usar
iconv
porque ele só lida com o Unicode válido-subconjunto do mais amplo (original) padrão UTF-8 padrão...Os nunbers envolvidos são bastante grandes, então eu testei as varreduras de alto e baixo alcance e diversas varreduras, aumentando em incrementos como 11111, 13579, 33333, 53441 ... Os resultados são todos iguais, agora tudo o que resta é testar a regex em relação a esses valores fora do intervalo do estilo UTF-8 (inválido para Unicode e, portanto, também inválido para o próprio UTF-8 estrito).
Aqui estão os módulos de teste:
fonte
\300\200
(muito ruim: esse é o ponto de código 0 não expresso com um byte nulo!). Eu acho que seu regexp os rejeita corretamente.Eu acho
uconv
(noicu-devtools
pacote Debian) útil para inspecionar dados UTF-8:(Os
\x
ajudam a identificar os caracteres inválidos (exceto o falso positivo introduzido voluntariamente com um literal\xE9
acima)).(muitos outros usos agradáveis).
fonte
recode
pode ser usado da mesma forma - exceto que eu acho que deveria falhar se solicitado a traduzir uma sequência multibyte inválida. Eu não tenho certeza;print...|recode u8..u8/x4
por exemplo , não falhará (o que faz um hexdump como você faz acima) porque não faz nadaiconv data data
, mas falha como fazrecode u8..u2..u8/x4
porque traduz e depois imprime. Mas não sei o suficiente para ter certeza - e há muitas possibilidades.test.txt
. Como devo encontrar o caractere inválido usando sua solução? O que significaus
no seu código?us
significa Estados Unidos, abreviação de ASCII. Ele converte a entrada em uma entrada ASCII na qual os caracteres não ASCII são convertidos em\uXXXX
notação e os não caracteres em\xXX
.Python teve um built-in
unicode
função desde a versão 2.0.No Python 3,
unicode
foi dobradostr
. Ele precisa receber um objeto semelhante a bytes , aqui osbuffer
objetos subjacentes para os descritores padrão .fonte
python 2
primeiro falha ao sinalizar não caracteres substitutos UTF-16 codificados em UTF-8 (pelo menos com 2.7.6).Me deparei com um problema semelhante (detalhe na seção "Contexto") e cheguei com a seguinte solução ftfy_line_by_line.py :
Usando codificar + substituir + ftfy para corrigir automaticamente o Mojibake e outras correções.
Contexto
Eu coletei> 10GiB CSV de metadados básicos do sistema de arquivos usando o seguinte script gen_basic_files_metadata.csv.sh , executando essencialmente:
O problema que tive foi com a codificação inconsistente de nomes de arquivos nos sistemas de arquivos, causando o
UnicodeDecodeError
processamento adicional com aplicativos python ( csvsql para ser mais específico).Portanto, eu apliquei acima do script ftfy, e levou
Observe que o ftfy é bem lento, o processamento daqueles> 10GiB levou:
enquanto sha256sum para comparação:
na CPU Intel (R) Core (i) i7-3520M a 2,90 GHz + 16 GiB de RAM (e dados na unidade externa)
fonte