Diferença entre [0-9], [[: dígito:]] e \ d

35

No artigo da Wikipedia sobre Expressões regulares , parece que [[:digit:]]= [0-9]= \d.

Quais são as circunstâncias em que eles não são iguais? Qual é a diferença?

Após algumas pesquisas, acho que uma diferença é que a expressão entre colchetes [:expr:]depende da localidade.

harbinn
fonte
3
O artigo da Wikipedia que você vinculou para responder à sua pergunta? Diferentes processadores / mecanismos de expressão regular suportam sintaxes diferentes para classes de caracteres (entre outras coisas).
Igal
O @igal wiki diz que há diferença, mas não dá muitos detalhes. Estou perguntando o detalhe, algo como isaac, disse thrig. Estou bastante interessado na diferença entre grep, sed, awk ... seja na versão GNU ou não.
harbinn

Respostas:

40

Sim, é [[:digit:]]~ [0-9]~ \d(onde ~ significa aproximado).
Na maioria das linguagens de programação (onde é suportado) \d[[:digit:]](idêntico).
O \dé menos comum que [[:digit:]](não no POSIX, mas no GNU grep -P).

Existem muitos dígitos no UNICODE , por exemplo:

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

Tudo isso pode ser incluído em [[:digit:]]ou \d.

Em vez disso, [0-9]geralmente são apenas os dígitos ASCII 0123456789.


Existem muitas linguagens: Perl, Java, Python, C. Na qual [[:digit:]](e \d) exige um significado estendido. Por exemplo, esse código perl corresponderá a todos os dígitos acima:

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

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

O que equivale a selecionar todos os caracteres que possuem as propriedades Unicode Numerice digits:

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

Qual grep poderia reproduzir (a versão específica do pcre pode ter uma lista interna diferente de pontos de código numérico que o Perl):

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

Altere para [0-9] para ver:

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

POSIX

Para o POSIX BRE ou ERE específico:
O \dnão é suportado (não no POSIX, mas no GNU grep -P). [[:digit:]]é exigido pelo POSIX para corresponder à classe de caracteres de dígito, que por sua vez é exigida pela ISO C como sendo os caracteres de 0 a 9 e nada mais. Assim, apenas em C locale tudo [0-9], [0123456789], \de [[:digit:]]significam exatamente a mesma. O [0123456789]não possui possíveis interpretações errôneas, [[:digit:]]está disponível em mais utilitários e é comum apenas significar [0123456789]. O \dé suportado por alguns utilitários.

Quanto a [0-9], o significado das expressões de intervalo é definido apenas pelo POSIX no código de idioma C; em outros locais, pode ser diferente (pode ser uma ordem de ponto de código ou ordem de intercalação ou outra coisa).

cartuchos

Algumas implementações podem entender um intervalo como algo diferente da ordem ASCII simples (ksh93 por exemplo):

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

E essa é uma fonte segura de bugs esperando para acontecer.

Isaac
fonte
Na prática, em sistemas POSIX iswctype()e BRE / ERE / curingas nos utilitários POSIX, [0-9] e [[: digit:]] correspondem apenas a 0123456789. E isso será explicitado na próxima revisão do padrão
Stéphane Chazelas
Eu não estava ciente de que perlé \dno modo Unicode combinados em dígitos decimais de outros scripts. Obrigado por isso. Com o PCRE, veja (*UCP)como no GNU grep -Po '(*UCP)\d'ou grep -Po '(*UCP)[[:digit:]]para que as classes sejam baseadas nas propriedades Unicode.
Stéphane Chazelas
Concordo que a [:digit:]sintaxe sugere que você deseja usar a localização, ou seja, o que o usuário considerar como um dígito. Eu nunca uso [:digit:]porque, na prática, é o mesmo que, [0-9]em qualquer caso, invariavelmente quero corresponder em 0123456789, nunca pretendo corresponder ٠١٢٣٤٥٦٧٨٩e não consigo pensar em um caso de uso em que alguém deseje corresponder em um dígito decimal em qualquer script com utilitários POSIX. Veja também a discussão atual sobre [:blank:]o zsh ML . Essas classes de personagens são um pouco confusas.
Stéphane Chazelas
13

Isso depende de como você define um dígito; [0-9]tende a ser apenas os ASCII (ou possivelmente algo que não seja ASCII nem um superconjunto de ASCII, mas com os mesmos 10 dígitos que em ASCII apenas com diferentes representações de bits (EBCDIC)); \dpor outro lado, pode ser apenas os dígitos simples (versões antigas do Perl, ou versões modernas do Perl com o /asinalizador de expressão regular ativado) ou pode ser uma correspondência Unicode da \p{Digit}qual é um conjunto maior de dígitos do que [0-9]ou /\d/acorrespondência.

$ 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 para obter mais informações ou consulte a documentação do idioma em questão para ver como ele se comporta.

Mas espere, tem mais! O código do idioma também pode variar o que \dcorresponde, portanto, \dpode corresponder a menos dígitos do que o conjunto Unicode completo desse tipo e (espero, geralmente) também inclui [0-9]. Isso é semelhante à diferença em C entre isdigit(3)( [0-9]) e isnumber(3)( [0-9mais qualquer outra coisa do código do idioma).

Pode haver chamadas que podem ser feitas para obter o valor do dígito, mesmo que não seja [0-9]:

$ perl -MUnicode::UCD=num -E 'say num(4)'
4
$ perl -MUnicode::UCD=num -E 'say num("\N{U+09EA}")'
4
$ 
agitar
fonte
Eu acho que isnumber()é uma coisa BSD, pelo menos com base na página homem parece tão
ilkkachu
Eu tenho um viés de BSD, sim
thrig
O sinalizador / a é um limitador específico para reduzir a lista de dígitos Unicode para corresponder apenas ... o modificador / a pode ser usado para forçar \ d a corresponder apenas ao ASCII de 0 a 9 .. Como tal, está forçando a corresponder exatamente à mesma e única [0-9].
Isaac
5

Significado diferente de [0-9], [[:digit:]]e \dsão apresentados em outras respostas. Aqui eu gostaria de adicionar diferenças na implementação do mecanismo regex.

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

Então [[:digit:]]sempre funciona , \ddepende. No manual do grep, é mencionado que [[:digit:]]é apenas 0-9no Clocal.

PS1: Se você souber mais, expanda a tabela.

PS2: GNU grep 3.1 e GNU 4.4 são usados ​​para teste.

harbinn
fonte
2
1) Existem muitas versões grepe sed, com a maior diferença provavelmente entre as versões GNU e outras. Essa resposta pode ser mais útil se mencionar a qual versão grepe a que sedela se refere. Ou qual é a fonte dessa tabela, nesse caso. 2) que a tabela pode muito bem ser transcrita para texto, uma vez que não contém qualquer coisa que exige que ele seja uma imagem
ilkkachu
@ilkkachu 1) o mais recente GNU grep 3.1 e GNU 4.4 é usado para teste. 2) Não sei como criar tabela. Parece que @ muru converteu a tabela em um formato de texto bonito.
harbinn
@harbinn Por favor, edite isso na sua resposta.
D.
@DanD. as informações da versão adicionadas. thx pela atenção
harbinn
1
Observe que o remódulo interno do python não suporta [[: digit:]], mas a biblioteca add in regexo suporta, então eu iria me incomodar um pouco com o que sempre funciona. Ele sempre funciona em situações de reclamação posix.
Steve Barnes
4

As diferenças teóricas já foram bem explicadas nas outras respostas, por isso resta explicar as diferenças práticas .

Aqui estão alguns dos casos de uso mais comuns para combinar um dígito:


Extração de dados de uma só vez

Freqüentemente, quando você deseja triturar alguns números, os próprios números estão em um arquivo de texto formatado de forma desajeitada. Você deseja extraí-los para uso em seu programa. Provavelmente, você pode dizer o formato do número (olhando o arquivo) e sua localidade atual; portanto, não há problema em usar qualquer um dos formulários , desde que o trabalho seja feito. \drequer o menor número de pressionamentos de tecla, por isso é muito comum.

Desinfecção de entrada

Você tem alguma entrada de usuário não confiável (talvez de um formulário da Web) e precisa garantir que ela não contenha surpresas. Talvez você queira armazená-lo em um campo numérico em um banco de dados ou use como parâmetro em um comando shell para executar em um servidor. Nesse caso, você realmente quer [0-9], já que é o mais restritivo e previsível.

Data de validade

Você tem alguns dados que não usará para nada "perigoso", mas seria bom saber se é um número. Por exemplo, seu programa permite que o usuário insira um endereço e você deseja destacar um possível erro de digitação se a entrada não contiver um número da casa. Nesse caso, você provavelmente quer ser o mais amplo possível, assim [[:digit:]]é o caminho a percorrer.


Esses seriam os três casos de uso mais comuns para correspondência de dígitos. Se você acha que eu perdi uma importante, por favor mande um comentário.

Baixo
fonte
bom trabalho, é problema de segurança relacionado, como Redos ou outros
frams