Wie greife ich nach allen Nicht-ASCII-Zeichen?

359

Ich habe mehrere sehr große XML-Dateien und versuche, die Zeilen zu finden, die Nicht-ASCII-Zeichen enthalten. Ich habe folgendes versucht:

grep -e "[\x{00FF}-\x{FFFF}]" file.xml

Dies gibt jedoch jede Zeile in der Datei zurück, unabhängig davon, ob die Zeile ein Zeichen im angegebenen Bereich enthält.

Habe ich die falsche Syntax oder mache ich etwas anderes falsch? Ich habe auch versucht:

egrep "[\x{00FF}-\x{FFFF}]" file.xml 

(mit einfachen und doppelten Anführungszeichen um das Muster).

pconrey
quelle
ASCII-Zeichen sind nur ein Byte lang. Wenn die Datei kein Unicode ist, sollten keine Zeichen über 0xFF liegen.
zdav
Wie gehen wir über \ xFF? Grep gibt den Fehler "grep: Bereich außerhalb der Reihenfolge in der Zeichenklasse" aus.
Mudit Jain

Antworten:

494

Sie können den folgenden Befehl verwenden:

grep --color='auto' -P -n "[\x80-\xFF]" file.xml

Dies gibt Ihnen die Zeilennummer und markiert Nicht-ASCII-Zeichen in Rot.

In einigen Systemen funktioniert das oben Gesagte abhängig von Ihren Einstellungen nicht, sodass Sie nach der Umkehrung greifen können

grep --color='auto' -P -n "[^\x00-\x7F]" file.xml

Beachten Sie auch, dass das wichtige Bit das -PFlag ist, das entspricht --perl-regexp: Es interpretiert Ihr Muster also als regulären Perl-Ausdruck. Das sagt es auch

Dies ist sehr experimentell und grep -P warnt möglicherweise vor nicht implementierten Funktionen.

Jerrymouse
quelle
42
Dies funktioniert in BSD grep(unter OS X 10.8 Mountain Lion) nicht, da die POption nicht unterstützt wird .
Bastiaan M. van de Weerd
20
Um meinen letzten Kommentar zu aktualisieren, ist die GNU-Version von grepin Homebrews dupesBibliothek verfügbar (aktivieren mit brew tap homebrew/dupes):brew install grep
Bastiaan M. van de Weerd
48
@BastiaanVanDeWeerd ist korrekt, grep unter OSX 10.8 unterstützt PCRE ("Perl-kompatible reguläre Ausdrücke") nicht mehr, da Darwin jetzt BSD grep anstelle von GNU grep verwendet. Eine Alternative zur Installation der dupesBibliothek besteht darin, pcrestattdessen Folgendes zu installieren : brew install pcre... Als Teil davon erhalten Sie das pcregrepDienstprogramm, das Sie wie folgt verwenden können:pcregrep --color='auto' -n "[\x80-\xFF]" file.xml
pvandenberk
15
Für Mac- brewBenutzer die Coreutils von GNU mit installiert werden brew install coreutils. Dadurch erhalten Sie viele GNU-Tools, denen ein 'g' vorangestellt ist - in diesem Fall verwenden ggrep. Dies sollte Probleme vermeiden, die durch das Ersetzen eines Systemdienstprogramms entstehen, da systemspezifische Mac-Skripte jetzt von BSD grep abhängen.
Joel Purra
22
Dies funktioniert gut auf einem Mac, den ag "[\x80-\xFF]" fileSie nur installieren müssenthe_silver_searcher
slf
123

Anstatt Annahmen über den Bytebereich von Nicht-ASCII-Zeichen zu treffen, wie es die meisten der oben genannten Lösungen tun, ist es IMO etwas besser, stattdessen explizit über den tatsächlichen Bytebereich von ASCII-Zeichen zu sprechen.

So würde die erste Lösung zum Beispiel lauten:

grep --color='auto' -P -n '[^\x00-\x7F]' file.xml

(Dies gilt grundsätzlich für alle Zeichen außerhalb des hexadezimalen ASCII-Bereichs: von \ x00 bis \ x7F)

Auf Mountain Lion funktioniert dies nicht (aufgrund der fehlenden PCRE-Unterstützung in BSD grep) , aber bei pcreInstallation über Homebrew funktioniert Folgendes genauso gut:

pcregrep --color='auto' -n '[^\x00-\x7F]' file.xml

Irgendwelche Vor- oder Nachteile, an die sich jeder denken kann?

pvandenberk
quelle
9
Dies funktionierte tatsächlich bei mir, wo die oben genannten Lösungen fehlschlugen. Es war nicht einfacher, M $ Word-Apostrophe zu finden!
AlbertEngelB
2
Wenn Sie eine Bash-kompatible Shell haben, aber nicht mit PCRE-Grep funktioniert, LC_COLLATE=C grep $'[^\1-\177]'funktioniert dies (für Dateien ohne Null-Bytes)
idupree
2
Diese Lösung scheint konsistenter zu funktionieren als die oben genannten.
0xcaff
1
Ich musste dies verwenden, um Kanji, Kyrillisch und Traditionelles Chinesisch in meiner UTF8-Datei aufzunehmen, wobei "[\ x80- \ xFF]" all dies verpasst hatte.
Buckaroo1177125
1
Der Profi ist, dass dies hervorragend funktioniert hat, während die anderen Optionen großartig waren, aber nicht so großartig. Bisher keine Nachteile gefunden.
jwpfox
67

Folgendes funktioniert für mich:

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

Nicht-ASCII-Zeichen beginnen bei 0x80 und gehen bei der Betrachtung von Bytes zu 0xFF. Grep (und Familie) führen keine Unicode-Verarbeitung durch, um Multibyte-Zeichen zu einer einzigen Entität zusammenzuführen, damit die Regex-Übereinstimmung Ihren Wünschen entspricht. Die -POption in meinem grep ermöglicht die Verwendung von \xddEscapezeichen in Zeichenklassen, um das zu erreichen, was Sie wollen.

Thelema
quelle
1
Führen Sie für die Ansicht, die möglicherweise nicht sofort weiß, wie dies über mehrere Dateien aufgerufen werden kann, einfach Folgendes aus: find. -name * .xml | xargs grep -P "[\ x80- \ xFF]"
David Mohundro
1
Dies gibt zwar eine Übereinstimmung zurück, es gibt jedoch keinen Hinweis darauf, was der Charakter ist und wo er sich befindet. Wie sieht man, was der Charakter ist und wo er ist?
Faheem Mitha
Wenn Sie das "-n" hinzufügen, erhalten Sie die Zeilennummer. Zusätzlich werden nicht sichtbare Zeichen als Block am Terminal angezeigt: grep -n -P "[\ x80- \ xFF]" file.xml
fooMonster
4
Ich habe ein Problem mit Hangul Koreanisch: echo '소녀시대' | grep -P "[\x80-\xFF]"Gibt nichts für mich zurück - kann jemand anderes bestätigen? (GNU grep 2.21)
frabjous
@frabjous Gleich hier, aber das Umkehren funktioniert umgekehrt : echo '소녀시대' | grep -P "[^\x00-\x7F]". Oder verwenden Sie einfach the_silver_searcherwie von @slf angegeben:echo '소녀시대' | ag "[\x80-\xFF]"
psmith
55

In Perl

perl -ane '{ if(m/[[:^ascii:]]/) { print  } }' fileName > newFile
noquery
quelle
1
Unter OSX10.11 musste ich mehrere grep + regex-Lösungen ausprobieren, bevor ich herausfand, dass dies tatsächlich funktioniert
sg
Möchten Sie diese OSX-Lösung @sg teilen?!
Geotheory
Das Perl-Skript oben ist die Lösung, über die ich spreche
sg
5
perl -lne 'print if /[^[:ascii:]]/' file.xml
Naveed
43

Der einfache Weg besteht darin, ein Nicht-ASCII-Zeichen zu definieren ... als ein Zeichen, das kein ASCII-Zeichen ist.

LC_ALL=C grep '[^ -~]' file.xml

Fügen Sie ^bei Bedarf nach dem eine Registerkarte hinzu .

Durch die Einstellung werden LC_COLLATE=Cböse Überraschungen über die Bedeutung von Zeichenbereichen in vielen Regionen vermieden. Die Einstellung LC_CTYPE=Cist erforderlich, um Einzelbytezeichen abzugleichen. Andernfalls würde der Befehl ungültige Bytesequenzen in der aktuellen Codierung übersehen. Durch die Einstellung werden LC_ALL=Cländerspezifische Effekte vollständig vermieden.

Gilles 'SO - hör auf böse zu sein'
quelle
Auf RedHat 6.4 mit tcsh musste ich <<< env LC_COLLATE = C grep -n '[^ - ~]' file.xml >>> verwenden. Ich habe -n hinzugefügt, um die Zeilennummer zu erhalten.
ddevienne
Für mich echo "A" | LC_COLLATE=C grep '[^ -~]'kehrt ein Match zurück
frabjous
1
@frabjous Wenn ja LC_ALL=en_US.UTF-8, übertrumpft dies die LC_COLLATEEinstellung. Sie sollten dies nicht in Ihrer Umgebung haben! LC_ALLbesteht normalerweise nur darin, eine bestimmte Aufgabe zu zwingen, ein bestimmtes Gebietsschema zu verwenden C. Um das Standardgebietsschema für alle Kategorien festzulegen, legen Sie fest LANG.
Gilles 'SO - hör auf böse zu sein'
1
Zuerst habe ich nicht hinzugefügt LC_ALL=C, es verhält sich unter Mac OS X und Ubuntu anders. Nachdem ich diese Einstellung hinzugefügt habe, erhalten sie das gleiche Ergebnis.
Max Peng
1
Dies funktioniert auf einem Mac, während die anderen grep-basierten Lösungen dies nicht tun.
Matthias Fripp
26

Hier ist eine andere Variante, die ich gefunden habe und die völlig andere Ergebnisse als die Grep-Suche [\x80-\xFF]in der akzeptierten Antwort hervorgebracht hat. Vielleicht ist es für jemanden nützlich, zusätzliche Nicht-ASCII-Zeichen zu finden:

grep --color='auto' -P -n "[^[:ascii:]]" myfile.txt

Hinweis: Der Grep meines Computers (ein Mac) hatte keine -POption, daher habe ich brew install grepden obigen Anruf mit ggrepstatt gestartet grep.

Ryanm
quelle
2
Dies ist bei weitem die beste Antwort, da es sowohl für Mac als auch für Linux funktioniert.
Tommy.carstensen
Nur eine, die für mich unter Linux funktioniert hat.
9

Der folgende Code funktioniert:

find /tmp | perl -ne 'print if /[^[:ascii:]]/'

Ersetzen Sie /tmpdurch den Namen des Verzeichnisses, das Sie durchsuchen möchten.

user7417071
quelle
2
Auf einem Mac funktioniert dies, während die meisten Grep-basierten dies nicht tun.
Matthias Fripp
9

Suche nach nicht druckbaren Zeichen. TLDR; Zusammenfassung

  1. Suche nach Steuerzeichen UND erweitertem Unicode
  2. Gebietsschemaeinstellung, z. B. LC_ALL=Cerforderlich, damit grep das tut, was Sie mit erweitertem Unicode erwarten

SO die bevorzugten Nicht-ASCII-Zeichensucher:

$ perl -ne 'print "$. $_" if m/[\x00-\x08\x0E-\x1F\x80-\xFF]/' notes_unicode_emoji_test

wie in der oberen Antwort, die inverse grep:

$ grep --color='auto' -P -n "[^\x00-\x7F]" notes_unicode_emoji_test

wie in der oberen Antwort, aber MIT LC_ALL=C:

$ LC_ALL=C grep --color='auto' -P -n "[\x80-\xFF]" notes_unicode_emoji_test

. . Mehr . . qualvolles Detail dazu :. . .

Ich stimme Harvey zu, der oben in den Kommentaren vergraben ist. Oft ist es sinnvoller, nach nicht druckbaren Zeichen zu suchen, oder es ist einfach, nicht ASCII zu denken, wenn Sie wirklich nicht druckbar denken sollten.Harvey schlägt "use this:" [^\n -~]"vor. Fügen Sie \ r für DOS-Textdateien hinzu. Das bedeutet"[^\x0A\x020-\x07E] "und fügen Sie \ x0D für CR" hinzu.

Außerdem ist das Hinzufügen von -c (Anzahl der übereinstimmenden Muster anzeigen) zu grep hilfreich, wenn nach nicht druckbaren Zeichen gesucht wird, da die übereinstimmenden Zeichenfolgen das Terminal durcheinander bringen können.

Ich fand, dass das Hinzufügen der Bereiche 0-8 und 0x0e-0x1f (zum Bereich 0x80-0xff) ein nützliches Muster ist. Dies schließt TAB, CR und LF sowie ein oder zwei weitere ungewöhnliche druckbare Zeichen aus. Meiner Meinung nach ist DIESES ein ziemlich nützliches (wenn auch grobes) Grep-Muster:

grep -c -P -n "[\x00-\x08\x0E-\x1F\x80-\xFF]" *

TATSÄCHLICH müssen Sie im Allgemeinen Folgendes tun:

LC_ALL=C grep -c -P -n "[\x00-\x08\x0E-\x1F\x80-\xFF]" *

Nervenzusammenbruch:

LC_ALL=C - set locale to C, otherwise many extended chars will not match (even though they look like they are encoded > 0x80)
\x00-\x08 - non-printable control chars 0 - 7 decimal
\x0E-\x1F - more non-printable control chars 14 - 31 decimal
\x80-1xFF - non-printable chars > 128 decimal
-c - print count of matching lines instead of lines
-P - perl style regexps

Instead of -c you may prefer to use -n (and optionally -b) or -l
-n, --line-number
-b, --byte-offset
-l, --files-with-matches

Beispiel: Ein praktisches Anwendungsbeispiel zum Suchen aller Dateien im aktuellen Verzeichnis:

LC_ALL=C find . -type f -exec grep -c -P -n "[\x00-\x08\x0E-\x1F\x80-\xFF]" {} + 

Möglicherweise möchten Sie den Grep manchmal anpassen. zB BS-Zeichen (0x08 - Rücktaste), das in einigen druckbaren Dateien verwendet wird oder um VT auszuschließen (0x0B - vertikale Registerkarte). In einigen Fällen können die Zeichen BEL (0x07) und ESC (0x1B) auch als druckbar angesehen werden.

Non-Printable ASCII Chars
** marks PRINTABLE but CONTROL chars that is useful to exclude sometimes
Dec   Hex Ctrl Char description           Dec Hex Ctrl Char description
0     00  ^@  NULL                        16  10  ^P  DATA LINK ESCAPE (DLE)
1     01  ^A  START OF HEADING (SOH)      17  11  ^Q  DEVICE CONTROL 1 (DC1)
2     02  ^B  START OF TEXT (STX)         18  12  ^R  DEVICE CONTROL 2 (DC2)
3     03  ^C  END OF TEXT (ETX)           19  13  ^S  DEVICE CONTROL 3 (DC3)
4     04  ^D  END OF TRANSMISSION (EOT)   20  14  ^T  DEVICE CONTROL 4 (DC4)
5     05  ^E  END OF QUERY (ENQ)          21  15  ^U  NEGATIVE ACKNOWLEDGEMENT (NAK)
6     06  ^F  ACKNOWLEDGE (ACK)           22  16  ^V  SYNCHRONIZE (SYN)
7     07  ^G  BEEP (BEL)                  23  17  ^W  END OF TRANSMISSION BLOCK (ETB)
8     08  ^H  BACKSPACE (BS)**            24  18  ^X  CANCEL (CAN)
9     09  ^I  HORIZONTAL TAB (HT)**       25  19  ^Y  END OF MEDIUM (EM)
10    0A  ^J  LINE FEED (LF)**            26  1A  ^Z  SUBSTITUTE (SUB)
11    0B  ^K  VERTICAL TAB (VT)**         27  1B  ^[  ESCAPE (ESC)
12    0C  ^L  FF (FORM FEED)**            28  1C  ^\  FILE SEPARATOR (FS) RIGHT ARROW
13    0D  ^M  CR (CARRIAGE RETURN)**      29  1D  ^]  GROUP SEPARATOR (GS) LEFT ARROW
14    0E  ^N  SO (SHIFT OUT)              30  1E  ^^  RECORD SEPARATOR (RS) UP ARROW
15    0F  ^O  SI (SHIFT IN)               31  1F  ^_  UNIT SEPARATOR (US) DOWN ARROW

UPDATE: Ich musste dies kürzlich noch einmal überprüfen. Und YYMV abhängig von Terminaleinstellungen / Sonnenwettervorhersage ABER. . Ich bemerkte, dass grep nicht viele Unicode- oder erweiterte Zeichen fand. Obwohl sie intuitiv mit dem Bereich von 0x80 bis 0xff übereinstimmen sollten, wurden 3- und 4-Byte-Unicode-Zeichen nicht abgeglichen. ??? Kann jemand das erklären? JA. @frabjous fragte und @calandoa erklärte dasLC_ALL=C dass das Gebietsschema für den Befehl festgelegt werden sollte, damit grep übereinstimmt.

zB mein Gebietsschema LC_ALL=leer

$ locale
LANG=en_IE.UTF-8
LC_CTYPE="en_IE.UTF-8"
.
.
LC_ALL=

grep mit LC_ALL=leeren Übereinstimmungen 2 Byte codierte Zeichen, aber nicht 3 und 4 Byte codiert:

$ grep -P -n "[\x00-\x08\x0E-\x1F\x80-\xFF]" notes_unicode_emoji_test
5 copyright c2a9
7:call  underscore c2a0
9:CTRL
31:5 © copyright
32:7 call  underscore

grep with LC_ALL=Cscheint mit allen erweiterten Zeichen übereinzustimmen , die Sie möchten:

$ LC_ALL=C grep --color='auto' -P -n "[\x80-\xFF]" notes_unicode_emoji_test  
1:���� unicode dashes e28090
3:��� Heart With Arrow Emoji - Emojipedia == UTF8? f09f9298
5:� copyright c2a9
7:call underscore c2a0
11:LIVE��E! ���������� ���� ���������� ���� �� �� ���� ����  YEOW, mix of japanese and chars from other e38182 e38184 . . e0a487
29:1 ���� unicode dashes
30:3 ��� Heart With Arrow Emoji - Emojipedia == UTF8 e28090
31:5  copyright
32:7 call underscore
33:11 LIVE��E! ���������� ���� ���������� ���� �� �� ���� ����  YEOW, mix of japanese and chars from other
34:52 LIVE��E! ���������� ���� ���������� ���� �� �� ���� ����  YEOW, mix of japanese and chars from other
81:LIVE��E! ���������� ���� ���������� ���� �� �� ���� ����  YEOW, mix of japanese and chars from other

DIESES Perl-Match (teilweise an anderer Stelle im Stackoverflow zu finden) ODER das inverse Grep in der oberen Antwort scheinen ALLE ~ seltsamen ~ und ~ wunderbaren ~ "Nicht-ASCII" -Zeichen zu finden, ohne das Gebietsschema festzulegen:

$ grep --color='auto' -P -n "[^\x00-\x7F]" notes_unicode_emoji_test

$ perl -ne 'print "$. $_" if m/[\x00-\x08\x0E-\x1F\x80-\xFF]/' notes_unicode_emoji_test  

1 ‐‐ unicode dashes e28090
3 💘 Heart With Arrow Emoji - Emojipedia == UTF8? f09f9298
5 © copyright c2a9
7 call  underscore c2a0
9 CTRL-H CHARS URK URK URK 
11 LIVEE! あいうえお かが アイウエオ カガ   ซฌ आइ  YEOW, mix of japanese and chars from other e38182 e38184 . . e0a487
29 1 ‐‐ unicode dashes
30 3 💘 Heart With Arrow Emoji - Emojipedia == UTF8 e28090
31 5 © copyright
32 7 call  underscore
33 11 LIVEE! あいうえお かが アイウエオ カガ   ซฌ आइ  YEOW, mix of japanese and chars from other
34 52 LIVEE! あいうえお かが アイウエオ カガ   ซฌ आइ  YEOW, mix of japanese and chars from other
73 LIVEE! あいうえお かが アイウエオ カガ   ซฌ आइ  YEOW, mix of japanese and chars from other

SO die bevorzugten Nicht-ASCII-Zeichensucher:

$ perl -ne 'print "$. $_" if m/[\x00-\x08\x0E-\x1F\x80-\xFF]/' notes_unicode_emoji_test

wie in der oberen Antwort, die inverse grep:

$ grep --color='auto' -P -n "[^\x00-\x7F]" notes_unicode_emoji_test

wie in der oberen Antwort, aber MIT LC_ALL=C:

$ LC_ALL=C grep --color='auto' -P -n "[\x80-\xFF]" notes_unicode_emoji_test
gaoithe
quelle
1
Antwort darauf, warum grep dank @calandoa und frabjous in den obigen Kommentaren zu Frage nicht mit Zeichen übereinstimmt, die in mehr als 2 Bytes codiert sind. Verwenden Sie LC_ALL = C vor dem Befehl grep.
Gaoithe
1
Vielen Dank, dass Sie sich die Mühe gemacht haben, eine Antwort zu veröffentlichen, die unter 800 anderen Upvotes vergraben ist! Mein Problem war ein 0x02-Zeichen. Vielleicht möchten Sie dieses "praktische Anwendungsbeispiel" ganz oben platzieren, da Sie wirklich nicht den gesamten Beitrag lesen müssen, um zu sehen, ob dies Ihr Problem ist.
Noumenon
1
Ich weiß, wirklich alte Antwort und exkrutierende Details, aber richtig nützlich für mich und andere, hoffe ich auch. Sie haben Recht, ich habe TLDR hinzugefügt; an der Spitze.
Gaoithe
1

Seltsamerweise musste ich das heute tun! Am Ende habe ich Perl verwendet, weil ich grep / egrep nicht zum Laufen bringen konnte (selbst im -P-Modus). Etwas wie:

cat blah | perl -en '/\xCA\xFE\xBA\xBE/ && print "found"'

\u2212Verwenden Sie für Unicode-Zeichen (wie im folgenden Beispiel) Folgendes:

find . ... -exec perl -CA -e '$ARGV = @ARGV[0]; open IN, $ARGV; binmode(IN, ":utf8"); binmode(STDOUT, ":utf8"); while (<IN>) { next unless /\N{U+2212}/; print "$ARGV: $&: $_"; exit }' '{}' \;
dty
quelle
1

Es könnte interessant sein zu wissen, wie man nach einem Unicode-Zeichen sucht. Dieser Befehl kann helfen. Sie müssen nur den Code in UTF8 kennen

grep -v $'\u200d'
Arezae
quelle
Ich bin nicht wirklich ein Experte, aber ich weiß genug, um zu wissen, dass dies keine UTF8-Darstellung ist, sondern UTF16 oder vielleicht UTF32 oder UCS16. Bei einem 2-Byte-Codepunkt sind diese drei möglicherweise alle gleich.
Baxissimo
1

Das Finden aller Nicht-ASCII-Zeichen erweckt den Eindruck, dass man entweder nach Unicode-Zeichenfolgen sucht oder beabsichtigt, diese Zeichen einzeln zu entfernen.

Versuchen Sie für erstere eine davon (Variable filewird für die Automatisierung verwendet):

 file=file.txt ; LC_ALL=C grep -Piao '[\x80-\xFF\x20]{7,}' $file | iconv -f $(uchardet $file) -t utf-8

 file=file.txt ; pcregrep -iao '[\x80-\xFF\x20]{7,}' $file | iconv -f $(uchardet $file) -t utf-8

 file=file.txt ; pcregrep -iao '[^\x00-\x19\x21-\x7F]{7,}' $file | iconv -f $(uchardet $file) -t utf-8

Vanilla grep funktioniert ohne LC_ALL = C nicht richtig, wie in den vorherigen Antworten angegeben.

ASCII-Bereich ist x00-x7F, Raum istx20 , da Zeichenfolgen Leerzeichen haben, wird es im negativen Bereich weggelassen.

Nicht-ASCII-Bereich ist x80-xFF, da Zeichenfolgen Leerzeichen haben, wird sie durch den positiven Bereich hinzugefügt.

Es wird angenommen, dass die Zeichenfolge mindestens 7 aufeinanderfolgende Zeichen innerhalb des Bereichs enthält. {7,}.

uchardet $fileGibt für eine Shell-lesbare Ausgabe eine Schätzung der Dateicodierung zurück, die zur automatischen Interpolation an iconv übergeben wird.

Niemand
quelle
Dies ist aufgrund der Erwähnung des uchardetBefehls sehr nützlich . Danke für das Heads-up!
bballdave025