Je dois détecter un fichier texte corrompu contenant des caractères utf-8, Unicode ou binaires invalides (non-ASCII).
�>t�ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½w�ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½o��������ï¿ï¿½_��������������������o����������������������￿����ß����������ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½~�ï¿ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½}���������}w��׿��������������������������������������ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½~������������������������������������_������������������������������������������������������������������������������^����ï¿ï¿½s�����������������������������?�������������ï¿ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½w�������������ï¿ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½}����������ï¿ï¿½ï¿½ï¿½ï¿½y����������������ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½o�������������������������}��
ce que j'ai essayé:
iconv -f utf-8 -t utf-8 -c file.csv
ceci convertit un fichier d'encodage utf-8 en encodage utf-8 et -c
permet d'ignorer les caractères utf-8 non valides. Cependant, à la fin, ces caractères illégaux sont toujours imprimés. Existe-t-il d'autres solutions dans bash sous linux ou dans d'autres langues?
En supposant que vos paramètres régionaux soient définis sur UTF-8, cela fonctionne bien pour reconnaître les séquences UTF-8 non valides:
grep -axv '.*' file.txt
Je voudrais grep
pour les caractères non ASCII.
Avec GNU grep avec pcre (à cause de -P
, pas toujours disponible. Sous FreeBSD, vous pouvez utiliser pcregrep dans le paquet pcre2) vous pouvez faire:
grep -P "[\x80-\xFF]" file
Référence dans Comment puis-je grep pour tous les caractères non-ASCII dans UNIX . Donc, en fait, si vous voulez seulement vérifier si le fichier contient des caractères non ASCII, vous pouvez simplement dire:
if grep -qP "[\x80-\xFF]" file ; then echo "file contains ascii"; fi
# ^
# silent grep
Pour supprimer ces caractères, vous pouvez utiliser:
sed -i.bak 's/[\d128-\d255]//g' file
Cela créera un fichier file.bak
en tant que sauvegarde, tandis que la file
d'origine aura ses caractères non ASCII supprimés. Référence dans Supprimez les caractères non-ascii de csv .
Ce que vous regardez est par définition corrompu. Apparemment, vous affichez le fichier tel qu’il est rendu en Latin-1; les trois caractères � représentent les trois valeurs d'octets 0xEF 0xBF 0xBD. Mais c’est le codage UTF-8 de Unicode REMPLACEMENT CHARACTER U + FFFD qui est le résultat de la tentative de conversion d’octets d’un codage inconnu ou indéfini en UTF-8 et qui s’afficherait correctement sous la forme (si vous avez un navigateur de ce siècle, vous devriez voir quelque chose comme un diamant noir avec un point d'interrogation, mais cela dépend aussi de la police que vous utilisez, etc.).
Votre question sur "comment détecter" ce phénomène est donc simple; le point de code Unicode U + FFFD est un cadeau mort et le seul symptôme possible du processus que vous indiquez.
Ce ne sont pas des "Unicodes invalides" ou des "UTF-8 invalides" en ce sens qu'il s'agit d'une séquence UTF-8 valide qui code un point de code Unicode valide; c'est juste que la sémantique de ce point de code particulier est "ceci est un caractère de remplacement pour un caractère qui ne pourrait pas être représenté correctement", c'est-à-dire une entrée invalide.
Pour ce qui est de la manière de la prévenir, la réponse est très simple, mais elle n’est pas non plus informative - vous devez identifier quand et comment un codage incorrect a eu lieu et réparer le processus qui a généré cette sortie invalide.
Pour supprimer simplement les caractères U + FFFD, essayez quelque chose comme
Perl -CSD -pe 's/\x{FFFD}//g' file
mais encore une fois, la bonne solution est de ne pas générer ces sorties erronées en premier lieu.
(Vous ne révélez pas le codage de vos données d'exemple. Il est possible qu'il soit corrompu supplémentaire. Si ce que vous nous montrez est un copier/coller du rendu UTF-8 des données, il a été "double-encodé". En d'autres termes, quelqu'un a pris - déjà corrompu, le texte UTF-8 ci-dessus et a demandé à l'ordinateur de le convertir du latin-1 au format UTF-8. "retour" en Latin 1. Ce que vous obtenez devrait alors être les données UTF-8 d'origine avant la conversion incorrecte superflue.)
Ce programme Perl devrait supprimer tous les caractères non-ASCII:
foreach $file (@ARGV) {
open(IN, $file);
open(OUT, "> super-temporary-utf8-replacement-file-which-should-never-be-used-EVER");
while (<IN>) {
s/[^[:ascii:]]//g;
print OUT "$_";
}
rename "super-temporary-utf8-replacement-file-which-should-never-be-used-EVER", $file;
}
Cela prend des fichiers comme entrée sur la ligne de commande, comme ceci:
Perl fixutf8.pl foo bar baz
Ensuite, pour chaque ligne, il remplace chaque instance d'un caractère non-ASCII par rien (suppression).
Il écrit ensuite cette ligne modifiée dans super-temporary-utf8-replacement-file-which-should-never-be-used-EVER
(nommé afin de ne modifier aucun autre fichier.)
Ensuite, il renomme le fichier temporaire en celui du fichier d'origine.
Ceci accepte TOUS les caractères ASCII (y compris DEL, NUL, CR, etc.), au cas où vous en auriez une utilisation spéciale. Si vous souhaitez uniquement des caractères imprimables, remplacez simplement :ascii:
par :print:
dans s///
.
J'espère que ça aide! S'il vous plaît laissez-moi savoir si ce n'est pas ce que vous cherchiez.
Je répète probablement ce que d'autres ont déjà dit. Mais je pense que vos caractères invalides sont toujours imprimés car ils sont peut-être valides. Le jeu de caractères universel est la tentative de référencer les caractères couramment utilisés dans le monde entier pour pouvoir écrire un logiciel robuste qui ne s'appuie pas sur un jeu de caractères spécial.
Donc, je pense que votre problème peut être l'un des deux suivants - en supposant que votre objectif général est de gérer cette entrée (malveillante) à partir de fichiers utf en général:
Donc, à mon avis, vous avez deux manières possibles de gérer cela:
iconv -f utf-8 -t ascii -o file_in_ascii.txt file_in_utf8.txt
. Mais être prudent transférer d'un espace de caractères plus large (utf) à un espace plus petit pourrait entraîner une perte de données.La manipulation de utf peut sembler délicate, les étapes suivantes peuvent vous aider à accomplir la tâche utf-readyness:
uconv
qui vous permet pour définir un gestionnaire de rappel pour les séquences non valides.Une solution très sale en python 3
import sys
with open ("cur.txt","r",encoding="utf-8") as f:
for i in f:
for c in i:
if(ord(c)<128):
print(c,end="")
Le résultat devrait être:
>two_o~}}w~_^s?w}yo}
Le programme C suivant détecte les caractères utf8 invalides . Il a été testé et utilisé sur un système Linux.
/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
void usage( void ) {
printf( "Usage: test_utf8 file ...\n" );
return;
}
int line_number = 1;
int char_number = 1;
char *file_name = NULL;
void inv_char( void ) {
printf( "%s: line : %d - char %d\n", file_name, line_number, char_number );
return;
}
int main( int argc, char *argv[]) {
FILE *out = NULL;
FILE *fh = NULL;
// printf( "argc: %d\n", argc );
if( argc < 2 ) {
usage();
exit( 1 );
}
// printf( "File: %s\n", argv[1] );
file_name = argv[1];
fh = fopen( file_name, "rb" );
if( ! fh ) {
printf( "Could not open file '%s'\n", file_name );
exit( 1 );
}
int utf8_type = 1;
int utf8_1 = 0;
int utf8_2 = 0;
int utf8_3 = 0;
int utf8_4 = 0;
int byte_count = 0;
int expected_byte_count = 0;
int cin = fgetc( fh );
while( ! feof( fh ) ) {
switch( utf8_type ) {
case 1:
if( (cin & 0x80) ) {
if( (cin & 0xe0) == 0xc0 ) {
utf8_1 = cin;
utf8_type = 2;
byte_count = 1;
expected_byte_count = 2;
break;
}
if( (cin & 0xf0) == 0xe0 ) {
utf8_1 = cin;
utf8_type = 2;
byte_count = 1;
expected_byte_count = 3;
break;
}
if( (cin & 0xf8) == 0xf0 ) {
utf8_1 = cin;
utf8_type = 2;
byte_count = 1;
expected_byte_count = 4;
break;
}
inv_char();
utf8_type = 1;
break;
}
break;
case 2:
case 3:
case 4:
// printf( "utf8_type - %d\n", utf8_type );
// printf( "%c - %02x\n", cin, cin );
if( (cin & 0xc0) == 0x80 ) {
if( utf8_type == expected_byte_count ) {
utf8_type = 1;
break;
}
byte_count = utf8_type;
utf8_type++;
if( utf8_type == 5 ) {
utf8_type = 1;
}
break;
}
inv_char();
utf8_type = 1;
break;
default:
inv_char();
utf8_type = 1;
break;
}
if( cin == '\n' ) {
line_number ++;
char_number = 0;
}
if( out != NULL ) {
fputc( cin, out );
}
// printf( "lno: %d\n", line_number );
cin = fgetc( fh );
char_number++;
}
fclose( fh );
return 0;
}
Essayez ceci, afin de trouver des caractères non-ASCII à partir du shell.
Commander:
$ Perl -ne 'print "$. $_" if m/[\x80-\xFF]/' utf8.txt
Sortie:
2 Pour être ou ne pas être
4 Byť či nebyť
5 是或不