Tengo un archivo de texto en una codificación desconocida o mixta. Quiero ver las líneas que contienen una secuencia de bytes que no es válida UTF-8 (canalizando el archivo de texto en algún programa). De manera equivalente, quiero filtrar las líneas que son válidas para UTF-8. En otras palabras, estoy buscando .grep [notutf8]
Una solución ideal sería portátil, corta y generalizable a otras codificaciones, pero si cree que la mejor manera es hornear en la definición de UTF-8 , continúe.
command-line
text-processing
character-encoding
unicode
Gilles 'SO- deja de ser malvado'
fuente
fuente
Respuestas:
Si quieres usar
grep
, puedes hacer:en configuraciones regionales UTF-8 para obtener las líneas que tienen al menos una secuencia UTF-8 no válida (esto funciona con GNU Grep al menos).
fuente
-a
, eso es necesario para que POSIX funcione. Sin embargo, GNUgrep
al menos no puede detectar el UTF-8 codificado UTF-16 sustituto no caracteres o puntos de código por encima de 0x10FFFF.-a
GNU lo necesitagrep
(supongo que no es compatible con POSIX). Con respecto al área sustituta y los puntos de código por encima de 0x10FFFF, este es un error (lo que podría explicar eso ). Para esto, agregar-P
debería funcionar con GNUgrep
2.21 (pero es lento); Tiene errores al menos en Debian grep / 2.20-4 .grep
es una utilidad de texto (solo se espera que funcione en la entrada de texto), por lo que supongo que el comportamiento de GNU grep es tan válido como cualquiera aquí.grep
(cuya intención es considerar las secuencias no válidas como no coincidentes) y posibles errores.Creo que probablemente quieras iconv . Es para convertir entre conjuntos de códigos y admite una cantidad absurda de formatos. Por ejemplo, para quitar cualquier cosa que no sea válida en UTF-8, podría usar:
iconv -c -t UTF-8 < input.txt > output.txt
Sin la opción -c, informará problemas al convertir a stderr, por lo que con la dirección del proceso podría guardar una lista de estos. Otra forma sería quitar las cosas que no son UTF8 y luego
diff input.txt output.txt
para obtener una lista de dónde se realizaron los cambios.
fuente
iconv -c -t UTF-8 <input.txt | diff input.txt - | sed -ne 's/^< //p'
. Sin embargo, no funcionará como una canalización, ya que necesita leer la entrada dos veces (no,tee
no lo hará, podría bloquearse dependiendo de la cantidad de almacenamiento en búfericonv
y dediff
hacer).diff <(iconv -c -t UTF-8 <input.txt) input.txt
Editar: he arreglado un error tipográfico en la expresión regular. Necesitaba un '\ x80` no \ 80 .
La expresión regular para filtrar formularios UTF-8 no válidos, para una adhesión estricta a UTF-8, es la siguiente
Salida (de líneas clave de la Prueba 1 ):
P. ¿Cómo se crean datos de prueba para probar una expresión regular que filtra Unicode no válido?
A. Cree su propio algoritmo de prueba UTF-8 y rompa sus reglas ...
Catch-22 .. Pero entonces, ¿cómo prueba entonces su algoritmo de prueba?
El regex, arriba, se ha probado (utilizando
iconv
como referencia) para cada valor entero desde0x00000
hasta0x10FFFF
... Este valor superior es el valor entero máximo de un punto de código UnicodeSegún esta página de Wikipedia UTF-8 ,.
Este numerador (1,112,064) equivale a un rango
0x000000
de0x10F7FF
, que es 0x0800 menos que el valor entero máximo real para el Punto de código Unicode más alto:0x10FFFF
Este bloque de enteros falta en el espectro de puntos de código Unicode, debido a la necesidad de que la codificación UTF-16 vaya más allá de su intención de diseño original a través de un sistema llamado pares sustitutos . Un bloque de
0x0800
enteros ha sido reservado para ser utilizado por UTF-16. Este bloque abarca el rango0x00D800
hasta0x00DFFF
. Ninguno de estos inteteres son valores Unicode legales y, por lo tanto, son valores UTF-8 no válidos.En la Prueba 1 ,
regex
se ha probado con cada número en el rango de puntos de código Unicode, y coincide exactamente con los resultados deiconv
... es decir. 0x010F7FF valores válidos y 0x000800 valores no válidos.Sin embargo, ahora surge el problema de: * ¿Cómo maneja la expresión regular el valor UTF-8 fuera de rango? anterior
0x010FFFF
(¿UTF-8 puede extenderse a 6 bytes, con un valor entero máximo de 0x7FFFFFFF ?Para generar los valores necesarios * no unicode de bytes UTF-8 , he usado el siguiente comando:
Para probar su validez (de alguna manera), he usado
Gilles'
UTF-8 regex ...La salida de 'perl's print chr' coincide con el filtrado de la expresión regular de Gilles. Uno refuerza la validez del otro ... No puedo usarlo
iconv
porque solo maneja el subconjunto válido Unicode Standard del UTF-8 más amplio (original) estándar...Los nunbers involucrados son bastante grandes, por lo que probé la parte superior del rango, la parte inferior del rango y varios escaneos escalonados en incrementos como 11111, 13579, 33333, 53441 ... Los resultados coinciden, así que ahora todo lo que queda es probar la expresión regular contra estos valores de estilo UTF-8 fuera de rango (inválido para Unicode y, por lo tanto, también inválido para UTF-8 estricto).
Aquí están los módulos de prueba:
fuente
\300\200
(realmente malo: ¡ese es el punto de código 0 no expresado con un byte nulo!). Creo que tu expresión regular los rechaza correctamente.Encuentro
uconv
(en elicu-devtools
paquete en Debian) útil para inspeccionar los datos UTF-8:(La
\x
ayuda s detecta los caracteres no válidos (excepto el falso positivo introducido voluntariamente con un literal\xE9
arriba)).(muchos otros buenos usos).
fuente
recode
se puede usar de manera similar, excepto que creo que debería fallar si se le pide traducir una secuencia multibyte no válida. Sin embargo, no estoy seguro; no fallará, porprint...|recode u8..u8/x4
ejemplo (que solo hace un volcado hexadecimal como lo hace anteriormente) porque no hace nadaiconv data data
, pero falla comorecode u8..u2..u8/x4
porque traduce y luego imprime. Pero no sé lo suficiente para estar seguro, y hay muchas posibilidades.test.txt
. ¿Cómo debo suponer encontrar el carácter inválido usando su solución? ¿Qué significaus
en tu código?us
significa Estados Unidos, que es la abreviatura de ASCII. Convierte la entrada en una ASCII donde los caracteres que no son ASCII se convierten en\uXXXX
notación y los que no son caracteres\xXX
.Python ha tenido una incorporado de
unicode
la función desde la versión 2.0.En Python 3,
unicode
se ha plegadostr
. Debe pasar un objeto similar a bytes , aquí losbuffer
objetos subyacentes para los descriptores estándar .fuente
python 2
que no marca UTF-8 codificado UTF-16 sustituto no caracteres (al menos con 2.7.6).Encontré un problema similar (detalle en la sección "Contexto") y llegué con la siguiente solución ftfy_line_by_line.py :
Usando encode + replace + ftfy para reparar automáticamente Mojibake y otras correcciones.
Contexto
He recopilado> 10GiB CSV de metadatos básicos del sistema de archivos usando el siguiente script gen_basic_files_metadata.csv.sh , ejecutándose esencialmente:
El problema que tuve fue con la codificación inconsistente de los nombres de archivos en los sistemas de archivos, lo que causó
UnicodeDecodeError
cuando se procesaba más con las aplicaciones de python ( csvsql para ser más específico).Por lo tanto, apliqué el script ftfy anterior, y tomó
Tenga en cuenta que ftfy es bastante lento, el procesamiento de esos> 10GiB tomó:
mientras sha256sum para la comparación:
en CPU Intel (R) Core (TM) i7-3520M @ 2.90GHz + 16GiB RAM (y datos en unidad externa)
fuente