web-dev-qa-db-de.com

wie man ungültige utf8-Unicode/-binärdateien in einer Textdatei erkennt

Ich muss beschädigte Textdateien erkennen, in denen ungültige (Nicht-ASCII-) utf-8-, Unicode- oder binäre Zeichen vorhanden sind. 

�>t�ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½w�ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½o��������ï¿ï¿½_��������������������o����������������������￿����ß����������ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½~�ï¿ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½}���������}w��׿��������������������������������������ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½~������������������������������������_������������������������������������������������������������������������������^����ï¿ï¿½s�����������������������������?�������������ï¿ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½w�������������ï¿ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½}����������ï¿ï¿½ï¿½ï¿½ï¿½y����������������ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½o�������������������������}��

was ich versucht habe:

iconv -f utf-8 -t utf-8 -c file.csv 

dadurch wird eine Datei von der utf-8-Codierung in die utf-8-Codierung konvertiert, und -c dient zum Überspringen ungültiger utf-8-Zeichen. Am Ende wurden diese illegalen Zeichen jedoch noch gedruckt. Gibt es andere Lösungen in bash für Linux oder andere Sprachen?

33
user121196

Angenommen, Sie haben das Gebietsschema auf UTF-8 gesetzt. Dies funktioniert gut, um ungültige UTF-8-Sequenzen zu erkennen:

grep -axv '.*' file.txt
36
Blaf

Ich würde grep für nicht ASCII Zeichen.

Mit GNU grep mit pcre (wegen -P, nicht immer verfügbar. Unter FreeBSD können Sie pcregrep im Paket pcre2 verwenden):

grep -P "[\x80-\xFF]" file

Referenz in Wie grep ich für alle Nicht-ASCII-Zeichen in UNIX . Wenn Sie also nur prüfen möchten, ob die Datei keine ASCII -Zeichen enthält, können Sie einfach sagen:

if grep -qP "[\x80-\xFF]" file ; then echo "file contains ascii"; fi
#        ^
#        silent grep

Um diese Zeichen zu entfernen, können Sie Folgendes verwenden:

sed -i.bak 's/[\d128-\d255]//g' file

Dadurch wird eine file.bak-Datei als Sicherungskopie erstellt, während für die ursprüngliche file keine Zeichen ASCII entfernt werden. Referenz in Nicht-ASCII-Zeichen aus csv entfernen.

9
fedorqui

Was Sie betrachten, ist per Definition beschädigt. Anscheinend zeigen Sie die Datei so an, wie sie in Latin-1 dargestellt wird. die drei Zeichen stehen für die drei Byte-Werte 0xEF 0xBF 0xBD. Dies ist jedoch die UTF-8-Kodierung des Unicode REPLACEMENT CHARACTER U + FFFD , die das Ergebnis des Versuchs ist, Bytes einer unbekannten oder undefinierten Kodierung in UTF-8 zu konvertieren, und die ordnungsgemäß als angezeigt würde (wenn Wenn Sie einen Browser aus diesem Jahrhundert haben, sollten Sie so etwas wie einen schwarzen Diamanten mit einem Fragezeichen darin sehen; dies hängt jedoch auch von der verwendeten Schriftart ab.

Daher ist Ihre Frage nach dem "Erkennen" dieses speziellen Phänomens einfach; Der Unicode-Codepunkt U + FFFD ist ein totes Giveaway und das einzig mögliche Symptom des von Ihnen implizierten Prozesses.

Dies sind nicht "ungültiger Unicode" oder "ungültiger UTF-8" in dem Sinne, dass dies eine gültige UTF-8-Sequenz ist, die einen gültigen Unicode-Codepunkt codiert. Es ist nur so, dass die Semantik dieses speziellen Codepunkts lautet: "Dies ist ein Ersatzzeichen für ein Zeichen, das nicht richtig dargestellt werden konnte", d. h. eine ungültige Eingabe.

Um dies zu verhindern, ist die Antwort wirklich einfach, aber auch wenig informativ. Sie müssen feststellen, wann und wie die falsche Kodierung stattgefunden hat, und den Prozess korrigieren, der zu dieser ungültigen Ausgabe geführt hat.

Um die U + FFFD-Zeichen einfach zu entfernen, versuchen Sie etwas wie

Perl -CSD -pe 's/\x{FFFD}//g' file

aber auch hier ist es die richtige Lösung, diese fehlerhaften Ausgaben gar nicht erst zu erzeugen.

(Sie offenbaren nicht die Kodierung Ihrer Beispieldaten. Es ist möglich, dass sie eine zusätzliche - Beschädigung aufweist. Wenn Sie uns eine Kopie des UTF-8-Rendering der Daten zeigen, war dies der Fall "double-encoded". Mit anderen Worten, jemand nahm UTF-8-Text an, der bereits wie oben beschrieben beschädigt war, und forderte den Computer auf, ihn von Latin-1 nach UTF-8 zu konvertieren. Das Rückgängigmachen ist einfach; konvertieren Sie einfach es "zurück" zu Latin-1. Was Sie erhalten sollten, sind die ursprünglichen UTF-8-Daten vor der überflüssigen fehlerhaften Konvertierung.)

4
tripleee

Dieses Perl-Programm sollte alle Nicht-ASCII-Zeichen entfernen:

 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;
}

Was dies bedeutet, ist, dass Dateien als Eingabe in die Befehlszeile verwendet werden, wie dies z.
Perl fixutf8.pl foo bar baz
Dann ersetzt es für jede Zeile jede Instanz eines Nicht-ASCII-Zeichens durch nichts (Löschen).
.__ Diese Zeile wird dann in super-temporary-utf8-replacement-file-which-should-never-be-used-EVER geschrieben (so benannt, dass keine anderen Dateien geändert werden.)
Anschließend wird die temporäre Datei in die der ursprünglichen Datei umbenannt.

Dies akzeptiert ALLE ASCII -Zeichen (einschließlich DEL, NUL, CR usw.), falls Sie eine spezielle Verwendung haben. Wenn Sie nur druckbare Zeichen wünschen, ersetzen Sie einfach :ascii: durch :print: in s///.

Ich hoffe das hilft! Bitte lassen Sie mich wissen, ob Sie nicht danach gesucht haben.

3
ASCIIThenANSI

Ich wiederhole wahrscheinlich, was andere schon gesagt haben. Aber ich denke, Ihre ungültig-Zeichen werden noch gedruckt, da sie möglicherweise gültig sind. Der Universal Character Set ist der Versuch, auf die weltweit häufig verwendeten Zeichen zu verweisen, um robuste Software schreiben zu können, die nicht auf einen speziellen Zeichensatz angewiesen ist.

Ich denke, Ihr Problem könnte eines der beiden sein - in der Annahme, dass Ihr allgemeines Ziel darin besteht, diese (böswilligen) Eingaben aus utf-Dateien im Allgemeinen zu behandeln:

  1. Es gibt invalid utf8-Zeichen (besser als ungültige Bytefolgen bezeichnet - dazu möchte ich auf den entsprechenden Wikipedia-Artikel verweisen.
  2. Es gibt abwesende Entsprechungen in Ihrer aktuellen Display-Schriftart, die durch ein spezielles Symbol ersetzt werden oder als binäres ASCII-Äquivalent angezeigt werden (z. B. möchte ich auf folgendes Post-Posting verweisen: UTF-8.) Sonderzeichen werden nicht angezeigt ).

Meines Erachtens haben Sie zwei Möglichkeiten, um damit umzugehen:

  1. Wandle die alle Zeichen von utf8 in etwas handhabbares um - z. B. ASCII - das kann man zB tun. mit iconv -f utf-8 -t ascii -o file_in_ascii.txt file_in_utf8.txt. Seien Sie jedoch vorsichtig, wenn der breitere Zeichenbereich (utf) in einen kleineren übertragen wird, was zu Datenverlust führen kann.
  2. Handle utf (8) korrekt - so schreibt die Welt Sachen. Wenn Sie der Meinung sind, dass Sie sich aufgrund eines begrenzenden Nachverarbeitungsschritts möglicherweise auf ASCII-Zeichen verlassen müssen, halten Sie an und überdenken Sie sie. In den meisten Fällen unterstützt der Postprozessor bereits utf. Es ist wahrscheinlich besser, herauszufinden, wie er verwendet werden kann. Sie machen Ihre Sachen zukunftssicher und kugelsicher.

Der Umgang mit utf mag schwierig erscheinen, die folgenden Schritte können Ihnen dabei helfen, utf-Readyness zu erreichen:

  • Sie können utf richtig anzeigen oder sicherstellen, dass Ihr Display-Stack (os, terminal usw.) eine ausreichende Teilmenge von Unicode anzeigen kann (die natürlich Ihren Anforderungen entsprechen sollte). Dies kann die Verwendung eines Sechskantfelds verhindern in vielen Fällen. Leider ist utf zu groß, um in einer Schriftart zu erscheinen, aber ein guter Anfangspunkt ist folgender Post: https://stackoverflow.com/questions/586503/complete-monospaced-unicode-font
  • Sie können ungültige Bytefolgen filtern. Um dies zu erreichen, gibt es viele Möglichkeiten. Dieser ul-Beitrag zeigt eine Vielzahl von Möglichkeiten: Ungültige Filterung von UTF8 - Ich möchte insbesondere die vierte Antwort hervorheben, die die Verwendung von uconv vorschlägt, die Sie verwenden können um einen Callback-Handler für ungültige Sequenzen festzulegen.
  • Lesen Sie mehr über Unicode.
1
florianb

Eine sehr schmutzige Lösung in 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="")

Die Ausgabe sollte sein:

>two_o~}}w~_^s?w}yo}
1
xitij

Das folgende C-Programm erkennt ungültige utf8-Zeichen . Es wurde getestet und auf einem Linux-System verwendet.

/*
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;
}
1
DBA

Versuchen Sie dies, um Nicht-ASCII-Zeichen in der Shell zu finden.

Befehl:

$ Perl -ne 'print "$. $_" if m/[\x80-\xFF]/'  utf8.txt

Ausgabe:

2 Pour être ou ne pas être
4 Byť či nebyť
5 是或不
0
Bouramas