Unterschied zwischen [0-9], [[: digit:]] und \ d

35

Im Wikipedia-Artikel über reguläre Ausdrücke scheint [[:digit:]]= [0-9]= \d.

Was sind die Umstände, unter denen sie nicht gleich sind? Was ist der Unterschied?

Nach einigen Recherchen denke ich, dass ein Unterschied darin besteht, dass der Klammerausdruck [:expr:]vom Gebietsschema abhängt.

Harbinn
quelle
3
Entspricht der Wikipedia-Artikel, den Sie verlinkt haben, nicht Ihrer Frage? Verschiedene Prozessoren / Engines für reguläre Ausdrücke unterstützen (unter anderem) unterschiedliche Syntaxen für Zeichenklassen.
igal
@igal Wiki sagt, dass es einen Unterschied gibt, gibt aber nicht viele Details. Ich frage nach dem Detail, so etwas wie Isaac, sagte Thrig. Ich bin ziemlich interessiert an ihrem Unterschied in grep, sed, awk ... ob GNU-Version oder nicht.
Harbinn

Antworten:

40

Ja, es ist [[:digit:]]~ [0-9]~ \d(wobei ~ ungefähr bedeutet).
In den meisten Programmiersprachen ( \dsofern unterstützt) ≡ [[:digit:]](identisch).
Das \dist weniger verbreitet als [[:digit:]](nicht in POSIX, aber es ist in GNU grep -P).

In UNICODE gibt es viele Ziffern , zum Beispiel:

123456789 # Hindu-Arabic arabische Ziffern
٠١٢٣٤٥٦٧٨٩ # ARABIC-INDIC
۰۱۲۳۴۵۶۷۸۹ # EXTENDED ARABIC-INDIC/PERSIAN
߀߁߂߃߄߅߆߇߈߉ # NKO DIGIT
०१२३४५६७८९ # DEVANAGARI

All dies kann in [[:digit:]]oder enthalten sein \d.

Stattdessen stehen [0-9]in der Regel nur die ASCII-Ziffern 0123456789.


Es gibt viele Sprachen: Perl, Java, Python, C. In denen [[:digit:]](und \d) nach einer erweiterten Bedeutung verlangt. Dieser Perl-Code stimmt beispielsweise mit allen Ziffern von oben überein:

$ a='0123456789 ٠١٢٣٤٥٦٧٨٩ ۰۱۲۳۴۵۶۷۸۹ ߀߁߂߃߄߅߆߇߈߉ ०१२३४५६७८९'

$ echo "$a" | perl -C -pe 's/[^\d]//g;' ; echo
0123456789٠١٢٣٤٥٦٧٨٩۰۱۲۳۴۵۶۷۸۹߀߁߂߃߄߅߆߇߈߉०१२३४५६७८९

Dies entspricht der Auswahl aller Zeichen mit den Unicode-Eigenschaften von Numericund digits:

$ echo "$a" | perl -C -pe 's/[^\p{Nd}]//g;' ; echo
0123456789٠١٢٣٤٥٦٧٨٩۰۱۲۳۴۵۶۷۸۹߀߁߂߃߄߅߆߇߈߉०१२३४५६७८९

Welches grep reproduzieren könnte (die spezifische Version von pcre hat möglicherweise eine andere interne Liste numerischer Codepunkte als Perl):

$ echo "$a" | grep -oP '\p{Nd}+'
0123456789
٠١٢٣٤٥٦٧٨٩
۰۱۲۳۴۵۶۷۸۹
߀߁߂߃߄߅߆߇߈߉
०१२३४५६७८९

Ändern Sie es auf [0-9], um Folgendes anzuzeigen:

$ echo "$a" | grep -o '[0-9]\+'
0123456789

POSIX

Für das spezifische POSIX BRE oder ERE:
Das \dwird nicht unterstützt (nicht in POSIX, sondern in GNU grep -P). [[:digit:]]wird von POSIX benötigt, um der Ziffernklasse zu entsprechen, die wiederum von ISO C benötigt wird, um die Zeichen 0 bis 9 und nichts anderes zu sein. Also nur in C locale alle [0-9], [0123456789], \dund [[:digit:]]bedeutet genau das gleiche. Das [0123456789]hat keine möglichen Fehlinterpretationen, [[:digit:]]ist in mehr Dienstprogrammen verfügbar und gemeinhin nur gemeint [0123456789]. Das \dwird von wenigen Dienstprogrammen unterstützt.

Was [0-9]ist die Bedeutung des Bereichs Ausdrücke nur von POSIX in der C - Sprache definiert; In anderen Ländern kann es anders sein (Codepoint-Reihenfolge oder Sortierreihenfolge oder etwas anderes).

Muscheln

Einige Implementierungen verstehen einen Bereich möglicherweise als etwas anderes als eine reine ASCII-Reihenfolge (z. B. ksh93):

$ LC_ALL=en_US.utf8 ksh -c 'a="'"$a"'";echo "${a//[0-9]}"'
  ۹ ߀߁߂߃߄߅߆߇߈߉ ९

Und das ist eine sichere Quelle von Fehlern, die darauf warten, passiert zu werden.

Isaac
quelle
In der Praxis auf POSIX-Systemen iswctype()und BRE / ERE / Wildcards in POSIX-Dienstprogrammen stimmen [0-9] und [[: digit:]] nur mit 0123456789 überein. Und das wird in der nächsten Überarbeitung des Standards
Stéphane Chazelas,
Ich war nicht bewusst , dass perl‚s \dim Unicode - Modus von anderen Skripten auf Dezimalstellen angepasst. Dank dafür. Informationen zu PCRE finden Sie (*UCP)in GNU grep -Po '(*UCP)\d'oder grep -Po '(*UCP)[[:digit:]]für Klassen, die auf Unicode-Eigenschaften basieren.
Stéphane Chazelas
Ich bin damit einverstanden, dass die [:digit:]Syntax nahe legt, dass Sie die Lokalisierung verwenden möchten. Dies ist alles, was der Benutzer als Ziffer ansieht. Ich benutze es nie, [:digit:]weil es in der Praxis das Gleiche ist [0-9]und auf jeden Fall, ich möchte immer mit 0123456789 übereinstimmen, ich möchte niemals mit übereinstimmen ٠١٢٣٤٥٦٧٨٩, und ich kann mir keinen Anwendungsfall vorstellen, in dem man mit einer Dezimalziffer übereinstimmen möchte in jedem Skript mit POSIX-Dienstprogrammen. Siehe auch die aktuelle Diskussion [:blank:]zum zsh ML . Diese Charakterklassen sind ein bisschen chaotisch.
Stéphane Chazelas
13

Dies hängt davon ab, wie Sie eine Ziffer definieren. [0-9]neigt dazu, nur die ASCII zu sein (oder möglicherweise etwas anderes, das weder ASCII noch eine Obermenge von ASCII ist, sondern dieselben 10 Stellen wie in ASCII nur mit unterschiedlichen Bitdarstellungen (EBCDIC)); \dauf der anderen Seite könnte entweder nur die einfachen Ziffern (alte Versionen von Perl oder modernen Versionen von Perl mit dem seine /aregulären Ausdruck Flag aktiviert) oder es könnte ein Unicode - Spiel von seinem \p{Digit}der ist eher eine größere Anzahl von Ziffern als [0-9]oder /\d/aSpiel.

$ perl -E 'say "match" if 42 =~ m/\d/'
match
$ perl -E 'say "match" if "\N{U+09EA}" =~ m/\d/'
match
$ perl -E 'say "match" if "\N{U+09EA}" =~ m/\d/a'
$ perl -E 'say "match" if "\N{U+09EA}" =~ m/[0-9]/'
$ 

perldoc perlrecharclass Weitere Informationen finden Sie in der Dokumentation der jeweiligen Sprache.

Aber warte, es gibt noch mehr! Das Gebietsschema kann auch variieren, welche Zeichen \dübereinstimmen, sodass möglicherweise \dweniger Zeichen als der gesamte Unicode-Satz davon übereinstimmen und (hoffentlich normalerweise) auch enthalten [0-9]. Dies ähnelt dem Unterschied in C zwischen isdigit(3)( [0-9]) und isnumber(3)( [0-9und was auch immer vom Gebietsschema abweicht).

Möglicherweise können Anrufe getätigt werden, um den Wert der Ziffer abzurufen, auch wenn dies nicht der Fall ist [0-9]:

$ perl -MUnicode::UCD=num -E 'say num(4)'
4
$ perl -MUnicode::UCD=num -E 'say num("\N{U+09EA}")'
4
$ 
Thrig
quelle
Ich denke, es isnumber()ist eine BSD-Sache, zumindest basierend auf der Manpage scheint es so
ilkkachu
Ich habe eine Art BSD-Voreingenommenheit, ja
am
Das / a-Flag ist ein spezifischer Begrenzer, um die Liste der Unicode-Ziffern so zu reduzieren , dass sie nur mit dem ASCII-Wert 0 bis 9 übereinstimmt . Als solches ist es zwingend, genau dasselbe und nur zuzuordnen [0-9].
Isaac
5

Unterschiedliche Bedeutung von [0-9], [[:digit:]]und \dsind in anderen Antworten präsentiert. Hier möchte ich Unterschiede in der Implementierung der Regex-Engine hinzufügen.

            [[:digit:]]    \d
grep -E               ✓     ×
grep -P               ✓     ✓
sed                   ✓     ×
sed -E                ✓     ×

Also klappt [[:digit:]]immer , kommt \ddrauf an. In greps Handbuch wird erwähnt, dass [[:digit:]]es sich nur 0-9um das CGebietsschema handelt.

PS1: Wenn Sie mehr wissen, erweitern Sie bitte die Tabelle.

PS2: GNU grep 3.1 und GNU 4.4 werden zum Testen verwendet.

Harbinn
quelle
2
1) Es gibt viele Versionen von grepund sed, mit dem größten Unterschied zwischen den GNU-Versionen im Vergleich zu anderen. Diese Antwort ist möglicherweise nützlicher, wenn angegeben wird, auf welche Version von grepund sedauf welche verwiesen wird. Oder wie die Quelle dieser Tabelle aussieht. 2)
Diese
@ilkkachu 1) Zum Testen wird das neueste GNU grep 3.1 und GNU 4.4 verwendet. 2) Ich kann keine Tabelle erstellen. Es scheint, dass @ muru die Tabelle in eine hübsche Textform umgewandelt hat.
Harbinn
@ harbinn Bitte bearbeite das in deine Antwort.
Dan D.
@DanD. die Versionsinfo hinzugefügt. Danke für die Aufmerksamkeit
Harbinn
1
Beachten Sie, dass das in Python eingebaute reModul [[: digit:]] nicht unterstützt, aber die Add-In-Bibliothek regexes unterstützt, so dass ich ein wenig an den immer funktionierenden Funktionen herumknabbern würde. Es funktioniert immer in Posix-Beschwerdesituationen.
Steve Barnes
4

Die theoretischen Unterschiede wurden bereits in den anderen Antworten ziemlich gut erklärt, so dass die praktischen Unterschiede noch zu erklären sind .

Hier sind einige der gängigsten Anwendungsfälle für den Abgleich einer Ziffer:


One-Shot-Datenextraktion

Wenn Sie einige Zahlen zusammenfassen möchten, befinden sich die Zahlen häufig in einer ungünstig formatierten Textdatei. Sie möchten sie zur Verwendung in Ihrem Programm extrahieren. Sie können sich wahrscheinlich das Zahlenformat sagen (um die Datei suchen) und aktuelle locale, so ist es in Ordnung , eine der Formen zu verwenden , solange er bekommt den Job getan. \derfordert die wenigsten Tastenanschläge, daher wird es sehr häufig verwendet.

Input-Desinfektion

Sie haben einige nicht vertrauenswürdige Benutzereingaben (möglicherweise über ein Webformular) und müssen sicherstellen, dass diese keine Überraschungen enthalten. Vielleicht möchten Sie es in einem numerischen Feld in einer Datenbank speichern oder als Parameter für einen Shell-Befehl verwenden, der auf einem Server ausgeführt werden soll. In diesem Fall möchten Sie wirklich [0-9], da es das restriktivste und vorhersehbarste ist.

Datenvalidierung

Sie haben einige Daten, die Sie nicht für "gefährliche" Zwecke verwenden werden, aber es wäre schön zu wissen, ob es sich um eine Zahl handelt. In Ihrem Programm kann der Benutzer beispielsweise eine Adresse eingeben, und Sie möchten einen möglichen Tippfehler hervorheben, wenn die Eingabe keine Hausnummer enthält. In diesem Fall möchten Sie wahrscheinlich so breit wie möglich sein [[:digit:]].


Dies scheinen die drei häufigsten Anwendungsfälle für die Ziffernanpassung zu sein. Wenn Sie der Meinung sind, dass ich ein wichtiges verpasst habe, schreiben Sie einen Kommentar.

Bass
quelle
Gute Arbeit, hängt mit Sicherheitsproblemen zusammen, wie ReDoS oder andere
Frams