Differenza tra [0-9], [[: digit:]] e \ d


35

Nell'articolo di Wikipedia sulle espressioni regolari , sembra che [[:digit:]]= [0-9]=\d .

Quali sono le circostanze in cui non sono uguali? Qual è la differenza?

Dopo alcune ricerche, penso che una differenza sia che l'espressione di parentesi [:expr:]dipende dalle impostazioni locali.


3
L' articolo di Wikipedia che hai collegato non risponde alla tua domanda? Processori / motori di espressioni regolari diversi supportano sintassi diverse per le classi di caratteri (tra le altre cose).
igal

@igal wiki dice che c'è differenza ma non fornisce molti dettagli. Sto chiedendo il dettaglio, qualcosa come Isaac, ha detto Thrig. Sono piuttosto interessato alla loro differenza in grep, sed, awk ... che sia la versione GNU o no.
Harbinn,

Risposte:


40

Sì, è [[:digit:]]~ [0-9]~ \d(dove ~ significa approssimativo).
Nella maggior parte dei linguaggi di programmazione (dove è supportato) \d[[:digit:]](identico).
L' \dè meno comune che [[:digit:]](non in POSIX ma è in GNU grep -P).

Esistono molte cifre in UNICODE , ad esempio:

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

Tutto ciò può essere incluso in [[:digit:]]o \d.

Invece, [0-9]è generalmente solo le cifre ASCII 0123456789.


Esistono molte lingue: Perl, Java, Python, C. In cui [[:digit:]](e \d) richiede un significato esteso. Ad esempio, questo codice perl corrisponderà a tutte le cifre dall'alto:

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

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

Che equivale a selezionare tutti i caratteri che hanno le proprietà Unicode di Numerice digits:

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

Quale grep potrebbe riprodurre (la versione specifica di pcre potrebbe avere un elenco interno diverso di punti di codice numerico rispetto a Perl):

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

Cambialo in [0-9] per vedere:

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

POSIX

Per POSIX BRE o ERE specifici:
The \dnon è supportato (non in POSIX ma è in GNU grep -P). [[:digit:]]POSIX deve corrispondere alla classe di caratteri delle cifre, che a sua volta è richiesta da ISO C per essere i caratteri da 0 a 9 e nient'altro. Quindi, solo in versione locale C tutto [0-9], [0123456789], \de [[:digit:]]dire esattamente la stessa cosa. Non [0123456789]ha possibili interpretazioni errate, [[:digit:]]è disponibile in più utility ed è comune significare solo [0123456789]. Il\d è supportato da poche utility.

Per quanto riguarda [0-9], il significato delle espressioni di intervallo è definito solo da POSIX nella locale C; in altri locali potrebbe essere diverso (potrebbe essere un ordine in codice o un ordine di confronto o qualcos'altro).

conchiglie

Alcune implementazioni potrebbero comprendere un intervallo come qualcosa di diverso dal semplice ordine ASCII (ad esempio ksh93):

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

E questa è una fonte sicura di bug in attesa di accadere.


In pratica sui sistemi POSIX iswctype()e BRE / ERE / caratteri jolly nelle utility POSIX, [0-9] e [[: digit:]] corrispondono solo a 0123456789. E ciò sarà reso esplicito nella prossima revisione dello standard
Stéphane Chazelas

Non ero consapevole del fatto che perl's \din modalità Unicode abbinate sul cifre decimali da altri script. Grazie per quello Con PCRE, vedi (*UCP)come in GNU grep -Po '(*UCP)\d'o grep -Po '(*UCP)[[:digit:]]per le classi basate sulle proprietà Unicode.
Stéphane Chazelas,

Sono d'accordo che la [:digit:]sintassi suggerirebbe che si desidera utilizzare la localizzazione, ovvero qualsiasi cosa l'utente consideri una cifra. Non lo uso mai [:digit:]perché in pratica è lo stesso di [0-9]e in ogni caso, invariabilmente voglio abbinare su 0123456789, non intendo mai abbinarlo ٠١٢٣٤٥٦٧٨٩, e non riesco a pensare a un caso d'uso in cui si vorrebbe abbinare su una cifra decimale in qualsiasi script con utilità POSIX. Vedi anche la discussione corrente [:blank:]su zsh ML . Quelle classi di personaggi sono un po 'un casino.
Stéphane Chazelas,

13

Questo dipende da come si definisce una cifra; [0-9]tende ad essere solo quelli ASCII (o forse qualcos'altro che non è né ASCII né un superset di ASCII ma le stesse 10 cifre di ASCII solo con rappresentazioni di bit diverse (EBCDIC)); \dd'altra parte potrebbero essere solo le cifre semplici (vecchie versioni di Perl o versioni moderne di Perl con il /aflag di espressione regolare abilitato) oppure potrebbe essere una corrispondenza Unicode di \p{Digit}cui è piuttosto un insieme più grande di cifre rispetto a [0-9]o /\d/amatch.

$ 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 per ulteriori informazioni o consultare la documentazione per la lingua in questione per vedere come si comporta.

Ma aspetta, c'è di più! Le impostazioni internazionali possono anche variare in base a ciò che \dcorrisponde, quindi \dpotrebbero corrispondere meno cifre rispetto al set Unicode completo di tali e (si spera, di solito) anche [0-9]. Questo è simile alla differenza in C tra isdigit(3)( [0-9]) e isnumber(3)( [0-9più qualsiasi altra cosa dalla locale).

È possibile che vengano effettuate chiamate per ottenere il valore della cifra, anche se non è [0-9]:

$ perl -MUnicode::UCD=num -E 'say num(4)'
4
$ perl -MUnicode::UCD=num -E 'say num("\N{U+09EA}")'
4
$ 

Penso che isnumber()sia una cosa di BSD, almeno in base alla pagina man sembra così
ilkkachu

Io ho qualcosa di un bias BSD, sì
thrig

Il flag / a è un limitatore specifico per ridurre l'elenco delle cifre Unicode affinché corrispondano solo ... il modificatore / a può essere utilizzato per forzare \ d a corrispondere solo a ASCII da 0 a 9 .. Come tale, sta forzando la corrispondenza esattamente uguale e unica [0-9].
Isaac,

5

Diverso significato di [0-9], [[:digit:]]e \dsono presentati in altre risposte. Qui vorrei aggiungere differenze nell'implementazione del motore regex.

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

Quindi [[:digit:]]funziona sempre , \ddipende. Nel manuale di grep è menzionato che [[:digit:]]è solo 0-9nel Clocale.

PS1: se ne conosci di più, espandi la tabella.

PS2: GNU grep 3.1 e GNU 4.4 vengono utilizzati per il test.


2
1) Esistono molte versioni di grepe sed, con la più grande differenza probabilmente tra le versioni GNU rispetto ad altre. Questa risposta potrebbe essere più utile se menzionasse quale versione di grepe sedsi riferisse a. O quale sia la fonte di quella tabella, del resto. 2) quella tabella potrebbe anche essere trascritta in testo, poiché non contiene nulla che richieda che sia un'immagine
ilkkachu

@ilkkachu 1) GNU grep 3.1 e GNU 4.4 vengono utilizzati per i test. 2) Non so come creare una tabella. Sembra che @ muru abbia convertito la tabella in un grazioso modulo di testo.
Harbinn,

@harbinn Modifica questo nella tua risposta.
Dan D.

@DanD. le informazioni sulla versione aggiunte. grazie per l'attenzione
Harbinn

1
Si noti che il remodulo integrato in Python non supporta [[: digit:]], ma la libreria add in regexlo supporta, quindi agiterei un po 'quando funziona sempre. Funziona sempre in situazioni di reclamo posix.
Steve Barnes,

4

Le differenze teoriche sono già state spiegate abbastanza bene nelle altre risposte, quindi resta da spiegare le differenze pratiche .

Ecco alcuni dei casi d'uso più comuni per abbinare una cifra:


Estrazione dei dati one-shot

Spesso, quando si desidera sgranocchiare alcuni numeri, i numeri stessi si trovano in un file di testo formattato in modo scomodo. Vuoi estrarli per usarli nel tuo programma. Probabilmente puoi dire il formato numerico (guardando il file) e la tua localizzazione corrente, quindi è ok usare uno qualsiasi dei moduli , purché finisca il lavoro. \drichiede il minor numero di tasti, quindi è molto comunemente usato.

Input sanificante

Hai qualche input dell'utente non attendibile (forse da un modulo web) e devi assicurarti che non contenga sorprese. Forse si desidera archiviarlo in un campo numerico in un database o utilizzare come parametro un comando shell da eseguire su un server. In questo caso, lo vuoi davvero [0-9], dal momento che è il più restrittivo e prevedibile.

Convalida dei dati

Hai un po 'di dati che non utilizzerai per nulla di "pericoloso", ma sarebbe bello sapere se si tratta di un numero. Ad esempio, il programma consente all'utente di inserire un indirizzo e si desidera evidenziare un possibile errore di battitura se l'input non contiene un numero civico. In questo caso, probabilmente vuoi essere il più ampio possibile, quindi [[:digit:]]è la strada da percorrere.


Sembrerebbero essere i tre casi d'uso più comuni per la corrispondenza delle cifre. Se pensi che ne abbia perso uno importante, lascia un commento.


bel lavoro, è legato ai problemi di sicurezza, come ReDoS o altri
corn
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.