Ich habe eine Textdatei mit unbekannter oder gemischter Codierung. Ich möchte die Zeilen sehen, die eine Byte-Sequenz enthalten, die nicht für UTF-8 gültig ist (indem ich die Textdatei in ein Programm weitergebe). Entsprechend möchte ich die Zeilen herausfiltern, die für UTF-8 gültig sind. Mit anderen Worten, ich suche .grep [notutf8]
Eine ideale Lösung wäre portabel, kurz und für andere Codierungen verallgemeinerbar. Wenn Sie jedoch der Meinung sind, dass die Definition von UTF-8 am besten funktioniert , fahren Sie fort.
command-line
text-processing
character-encoding
unicode
Gilles 'SO - hör auf böse zu sein'
quelle
quelle
Antworten:
Wenn Sie verwenden möchten
grep
, können Sie Folgendes tun:in UTF-8-Gebietsschemas, um die Zeilen mit mindestens einer ungültigen UTF-8-Sequenz abzurufen (dies funktioniert mindestens mit GNU Grep).
quelle
-a
ist dies erforderlich, um mit POSIX zu arbeiten. GNUgrep
erkennt jedoch zumindest die UTF-8-codierten UTF-16-Ersatzzeichen, die keine Zeichen oder Codepunkte über 0x10FFFF sind, nicht.-a
von GNU benötigtgrep
(was vermutlich nicht POSIX-konform ist). In Bezug auf dem Surrogat - Bereich und die Codepoints über 0x10FFFF, das ein Fehler dann (was erklären könnte , dass ). Zu diesem Zweck-P
sollte das Hinzufügen mit GNUgrep
2.21 funktionieren (ist aber langsam). es ist zumindest in Debian grep / 2.20-4 fehlerhaft .grep
es sich um ein Textdienstprogramm handelt (das nur für die Texteingabe geeignet ist). Ich nehme also an, dass das Verhalten von GNU grep so gültig ist wie jedes andere hier.grep
(deren Absicht es ist, ungültige Sequenzen als nicht übereinstimmend zu betrachten) und mögliche Fehler zu kennen.Ich denke, Sie möchten wahrscheinlich iconv . Es dient zum Konvertieren zwischen Codesätzen und unterstützt eine absurde Anzahl von Formaten. Zum Entfernen von Inhalten, die in UTF-8 nicht gültig sind, können Sie beispielsweise Folgendes verwenden:
iconv -c -t UTF-8 < input.txt > output.txt
Ohne die Option -c werden Probleme bei der Konvertierung nach stderr gemeldet. In Bezug auf die Prozessrichtung können Sie eine Liste dieser Probleme speichern. Ein anderer Weg wäre, das nicht-UTF8-Zeug zu entfernen und dann
diff input.txt output.txt
Eine Liste der Stellen, an denen Änderungen vorgenommen wurden.
quelle
iconv -c -t UTF-8 <input.txt | diff input.txt - | sed -ne 's/^< //p'
. Es wird nicht funktionieren wie eine Pipeline, obwohl, da Sie die Eingabe zweimal lesen müssen (nein,tee
geht nicht, es könnte Block je nachdem , wie viel Pufferungiconv
unddiff
tun).diff <(iconv -c -t UTF-8 <input.txt) input.txt
Bearbeiten: Ich habe einen Tippfehler in der Regex behoben. Es brauchte ein "\ x80" nicht \ 80 .
Der reguläre Ausdruck zum Herausfiltern ungültiger UTF-8-Formulare zur strikten Einhaltung von UTF-8 lautet wie folgt
Ausgabe (von Schlüsselzeilen aus Test 1 ):
Frage: Wie erstellt man Testdaten, um einen regulären Ausdruck zu testen, der ungültigen Unicode filtert?
A. Erstellen Sie Ihren eigenen UTF-8-Testalgorithmus und brechen Sie seine Regeln ...
Catch-22 .. Aber wie testen Sie dann Ihren Testalgorithmus?
Der oben angegebene reguläre Ausdruck wurde (unter Verwendung
iconv
als Referenz) für jeden ganzzahligen Wert von0x00000
bis getestet0x10FFFF
. Dieser obere Wert ist der maximale ganzzahlige Wert eines Unicode-CodepunktsLaut dieser wikipedia UTF-8 Seite.
Diese Zahl (1.112.064) entspricht einem Bereich
0x000000
bis0x10F7FF
, der 0x0800 vor dem tatsächlichen maximalen Ganzzahlwert für den höchsten Unicode-Codepunkt liegt:0x10FFFF
Dieser Block von ganzen Zahlen aus dem Unicode - Codepunkten Spektrum fehlt, wegen der Notwendigkeit für die UTF-16 - Kodierung zu Schritt über seine ursprüngliche Konstruktionsabsicht über ein System namens Ersatzpaar . Ein
0x0800
Ganzzahlblock wurde reserviert, um von UTF-16 verwendet zu werden. Dieser Block erstreckt sich über den Bereich0x00D800
bis0x00DFFF
. Keiner dieser Inteter ist ein zulässiger Unicode-Wert und daher ein ungültiger UTF-8-Wert.In Test 1 wurde das
regex
gegen jede Zahl im Bereich der Unicode-Codepunkte getestet, und es stimmt genau mit den Ergebnissen von übereiniconv
. Gültige Werte 0x010F7FF und ungültige Werte 0x000800 .Es stellt sich jedoch die Frage, * wie der Regex mit UTF-8-Werten außerhalb des Bereichs umgeht. oben
0x010FFFF
(UTF-8 kann sich auf 6 Byte mit einem maximalen Integer-Wert von 0x7FFFFFFF erstrecken .Um die erforderlichen * Nicht-Unicode-UTF-8-Byte-Werte zu generieren , habe ich den folgenden Befehl verwendet:
Um ihre Gültigkeit (in gewisser Weise) zu testen, habe ich
Gilles'
UTF-8-Regex verwendet ...Die Ausgabe von 'perl's print chr' entspricht der Filterung von Gilles 'regulärem Ausdruck. Einer verstärkt die Gültigkeit des anderen. Ich kann ihn nicht verwenden,
iconv
da er nur die gültige Unicode-Standard-Teilmenge der breiteren (ursprünglichen) UTF-8-Datei verarbeitet Standard...Die beteiligten Nonnen sind ziemlich groß, daher habe ich das obere und untere Messfeld getestet und mehrere Scans in Schritten wie 11111, 13579, 33333, 53441 durchgeführt. Die Ergebnisse stimmen also jetzt überein Alles, was bleibt, ist das Testen der Regex anhand dieser UTF-8-Werte außerhalb des gültigen Bereichs (ungültig für Unicode und daher auch für das strikte UTF-8 selbst).
Hier sind die Testmodule:
quelle
\300\200
. Ich denke, dass Ihr regulärer Ausdruck sie richtig ablehnt.Ich finde
uconv
(imicu-devtools
Paket in Debian) nützlich, um UTF-8-Daten zu überprüfen:(Die
\x
s helfen beim Erkennen der ungültigen Zeichen (mit Ausnahme des freiwillig mit einem\xE9
obigen Literal eingeführten Falschpositivs ).(viele andere nette Verwendungen).
quelle
recode
kann ähnlich verwendet werden - außer dass ich denke, es sollte fehlschlagen, wenn eine ungültige Multibyte-Sequenz übersetzt werden soll. Ich bin mir aber nicht sicher; es wird nicht für nichtprint...|recode u8..u8/x4
zum Beispiel (die nur eine hexdump tut , wie Sie oben tun) , weil es nichts tut , abericonv data data
, aber es ist nicht wie ,recode u8..u2..u8/x4
weil es dann druckt übersetzt. Aber ich weiß nicht genug darüber - und es gibt viele Möglichkeiten.test.txt
. Wie soll ich annehmen, um das ungültige Zeichen unter Verwendung Ihrer Lösung zu finden? Was bedeutetus
in Ihrem Code?us
bedeutet USA, das ist die Abkürzung für ASCII. Es konvertiert die Eingabe in eine ASCII-Eingabe, bei der die Nicht-ASCII-Zeichen in die\uXXXX
Notation und die Nicht-Zeichen in die Notation konvertiert werden\xXX
.Python verfügt seit Version 2.0 über eine integrierte
unicode
Funktion .In Python 3
unicode
wurde in gefaltetstr
. Es muss ein byteartiges Objekt übergeben werden , hier die zugrunde liegendenbuffer
Objekte für die Standarddeskriptoren .quelle
python 2
eine kann UTF-8-codierte UTF-16-Ersatzzeichen nicht kennzeichnen (mindestens 2.7.6).Ich bin auf ein ähnliches Problem gestoßen (Details im Abschnitt "Kontext") und bin mit der folgenden ftfy_line_by_line.py- Lösung angekommen :
Verwenden Sie encode + replace + ftfy , um Mojibake und andere Korrekturen automatisch zu korrigieren.
Kontext
Ich habe mit dem folgenden Skript gen_basic_files_metadata.csv.sh > 10 GB CSV von grundlegenden Metadaten des Dateisystems gesammelt und im Wesentlichen ausgeführt:
Das Problem, das ich hatte, war die inkonsistente Codierung von Dateinamen in verschiedenen Dateisystemen, was dazu führte, dass die
UnicodeDecodeError
Verarbeitung mit Python-Anwendungen ( genauer gesagt mit csvsql ) fortgesetzt wurde .Deshalb habe ich über ftfy Skript angewendet, und es hat gedauert
Bitte beachten Sie, dass ftfy ziemlich langsam ist und die Verarbeitung von> 10 GB dauerte:
während sha256sum zum Vergleich:
auf Intel (R) Core (TM) i7-3520M-CPU bei 2,90 GHz + 16 Gb RAM (und Daten auf externem Laufwerk)
quelle