J'ai un fichier texte dans un encodage inconnu ou mixte. Je souhaite afficher les lignes contenant une séquence d'octets non valide UTF-8 (en transférant le fichier texte dans un programme). De manière équivalente, je veux filtrer les lignes qui sont valides UTF-8. En d'autres termes, je cherche .grep [notutf8]
Une solution idéale serait portable, courte et généralisable à d'autres encodages, mais si vous sentez que le meilleur moyen est de cuire au four dans la définition de UTF-8 , continuez.
command-line
text-processing
character-encoding
unicode
Gilles, arrête de faire le mal
la source
la source
Réponses:
Si vous voulez utiliser
grep
, vous pouvez faire:dans les environnements locaux UTF-8 pour obtenir les lignes ayant au moins une séquence UTF-8 non valide (cela fonctionne au moins avec GNU Grep).
la source
-a
, c'est nécessaire pour travailler avec POSIX. Cependant,grep
au moins, GNU ne parvient pas à repérer les non-caractères ou les points de code de substitution UTF-16 codés par UTF-8 supérieurs à 0x10FFFF.-a
est nécessaire pour GNUgrep
(ce qui n’est pas compatible POSIX, je suppose). En ce qui concerne la zone de substitution et les points de code supérieurs à 0x10FFFF, il s'agit alors d'un bogue (ce qui pourrait expliquer cela ). Pour cela, l’ajout-P
devrait fonctionner avec GNUgrep
2.21 (mais est lent); il est au moins bogué dans Debian grep / 2.20-4 .grep
s'agit d'un utilitaire de texte (qui ne devrait fonctionner que sur la saisie de texte). Je suppose donc que le comportement de GNU grep est aussi valable que n'importe lequel ici.grep
(dont l’intention est de considérer les séquences non valides comme non concordantes) et les éventuels bogues.Je pense que vous voulez probablement iconv . C'est pour la conversion entre les jeux de codes et supporte un nombre absurde de formats. Par exemple, pour supprimer tout élément non valide dans UTF-8, vous pouvez utiliser:
iconv -c -t UTF-8 < input.txt > output.txt
Sans l'option -c, les problèmes de conversion en stderr seront signalés. Vous pouvez donc en enregistrer une liste avec la direction du processus. Une autre solution consisterait à supprimer les éléments non-UTF8, puis
diff input.txt output.txt
pour une liste des endroits où des modifications ont été apportées.
la source
iconv -c -t UTF-8 <input.txt | diff input.txt - | sed -ne 's/^< //p'
. Il ne fonctionnera pas comme un pipeline, cependant, puisque vous devez lire l'entrée deux fois (non,tee
ne le fera pas, il risque de bloquer selon la quantité de mémoire tamponiconv
etdiff
faire).diff <(iconv -c -t UTF-8 <input.txt) input.txt
Edit: J'ai corrigé un typo-bug dans la regex .. Il avait besoin d'un '\ x80` pas \ 80 .
La regex permettant de filtrer les formulaires UTF-8 non valides, pour une stricte adhésion à UTF-8, est la suivante
Sortie (des lignes clés du test 1 ):
Q. Comment créer des données de test pour tester une expression régulière qui filtre Unicode non valide?
A. Créez votre propre algorithme de test UTF-8 et enfreignez ses règles ...
Catch-22 .. Mais comment alors tester votre algorithme de test?
L'expression rationnelle ci-dessus a été testée (en utilisant
iconv
comme référence) pour chaque valeur entière comprise entre0x00000
et0x10FFFF
.. Cette valeur supérieure est la valeur entière maximale d'un point de codage Unicode.Selon cette page wikipedia UTF-8 , .
Ce nombre (1 112 064) équivaut à une plage
0x000000
de0x10F7FF
, ce qui correspond à 0x0800 de la valeur entière maximale réelle pour le point de codage Unicode le plus élevé:0x10FFFF
Ce bloc d’entiers est absent du spectre des points de codage Unicode, car le codage UTF-16 doit dépasser l’intention de conception initiale du système via un système appelé paires de substitution . Un bloc d'
0x0800
entiers a été réservé pour être utilisé par UTF-16 .. Ce bloc couvre la plage0x00D800
de0x00DFFF
. Aucun de ces inteters ne sont des valeurs Unicode légales et sont donc des valeurs UTF-8 non valides.Dans le test 1 , le
regex
a été testé sur tous les nombres de la plage de codecs Unicode et correspond exactement aux résultats deiconv
.. ie. 0x010F7FF valeurs valides et 0x000800 valeurs non valides.Cependant, le problème se pose maintenant de, * Comment l’expression rationnelle gère-t-elle la valeur UTF-8 hors plage; ci
0x010FFFF
- dessus (UTF-8 peut s'étendre sur 6 octets, avec une valeur entière maximale de 0x7FFFFFFF ?Pour générer les valeurs d'octet * non-unicode UTF-8 nécessaires , j'ai utilisé la commande suivante:
Pour tester leur validité (d'une certaine manière), j'ai utilisé
Gilles'
UTF-8 regex ...La sortie de 'perl's print chr' correspond au filtrage des regex de Gilles. L'une renforce la validité de l'autre .. Je ne peux pas l'utiliser
iconv
car elle ne gère que le sous-ensemble valide-Unicode Standard de l'UTF-8 plus large (original). la norme...Les monstres impliqués sont plutôt volumineux. J'ai donc testé les balayages haut de gamme, bas de gamme et plusieurs pas à pas, par exemple, 11111, 13579, 33333, 53441 ... Les résultats concordent, alors maintenant il ne reste plus qu'à tester l'expression rationnelle par rapport à ces valeurs de style UTF-8 hors limites (non valides pour Unicode, et donc également non valides pour le strict UTF-8 lui-même).
Voici les modules de test:
la source
\300\200
(vraiment mauvais: c’est le point de code 0 non exprimé avec un octet nul!). Je pense que votre expression rationnelle les rejette correctement.Je trouve
uconv
(dans leicu-devtools
paquet dans Debian) utile d’inspecter les données UTF-8:(L’
\x
aide s permet de repérer les caractères non valides (à l’exception du faux positif introduit volontairement avec un littéral\xE9
ci-dessus)).(plein d'autres bonnes utilisations).
la source
recode
on peut utiliser la même chose - sauf que je pense que cela devrait échouer s’il est demandé de traduire une séquence multi-octets non valide. Je ne suis pas sûr cependant; cela n'échouera pas,print...|recode u8..u8/x4
par exemple (ce qui se produira comme un hexdump comme ci-dessus) car il ne fait rieniconv data data
, mais il échoue commerecode u8..u2..u8/x4
s'il traduisait puis imprimait. Mais je n'en sais pas assez pour en être sûr - et il y a beaucoup de possibilités.test.txt
. Comment devrais-je supposer trouver le caractère non valide à l'aide de votre solution? Que signifieus
dans votre code?us
signifie États-Unis, c'est-à-dire ASCII. Il convertit l'entrée en une entrée ASCII dans laquelle les caractères non-ASCII sont convertis en\uXXXX
notation et les non-caractères en\xXX
.Python a eu intégré la
unicode
fonction depuis la version 2.0.En Python 3,
unicode
a été plié enstr
. Il faut lui transmettre un objet de type octet , ici lesbuffer
objets sous-jacents des descripteurs standard .la source
python 2
un ne parvient pas à signaler les non-caractères de substitution UTF-16 codés en UTF-8 (au moins avec 2.7.6).Je suis tombé sur un problème similaire (détail dans la section "Contexte") et je suis arrivé avec la solution suivante ftfy_line_by_line.py :
Utilisation de encode + replace + ftfy pour corriger automatiquement Mojibake et d’autres corrections.
Le contexte
J'ai collecté> 10GiB CSV de métadonnées de base d'un système de fichiers à l'aide du script suivant gen_basic_files_metadata.csv.sh , qui s'exécute essentiellement:
Le problème que j'ai eu était avec le codage incohérent des noms de fichiers à travers les systèmes de fichiers, ce
UnicodeDecodeError
qui entraînait un traitement ultérieur avec les applications python ( csvsql étant plus spécifique).J'ai donc appliqué le script ftfy ci-dessus, et il a fallu
Veuillez noter que ftfy est assez lent, le traitement de ces> 10GiB a pris:
tandis que sha256sum à titre de comparaison:
sur processeur Intel (R) Core (TM) i7-3520M avec 2.90 GHz + 16 Go de RAM (et données sur lecteur externe)
la source