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

28

Em 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 de colchete [:expr:] é dependente de localidade.

    
por harbinn 02.01.2018 / 04:01

4 respostas

37

Sim, é [[:digit:]] ~ [0-9] ~ \d (onde ~ significa aproximada).
Na maioria das linguagens de programação (onde é suportado) \d[[:digit:]] (identico).
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

Todos os podem ser incluídos em [[:digit:]] ou \d .

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

Existem muitos idiomas: Perl, Java, Python, C. Em que [[: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٠١٢٣٤٥٦٧٨٩۰۱۲۳۴۵۶۷۸۹߀߁߂߃߄߅߆߇߈߉०१२३४५६७८९

Qual é o equivalente a selecionar todos os caracteres com as propriedades Unicode de Numeric e digits :

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

Que grep poderia reproduzir (a versão específica do pcre pode ter uma lista interna diferente de pontos de código numérico que 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 \d não é suportado (não no POSIX, mas no GNU grep -P ). [[:digit:]] é exigido pelo POSIX para corresponder à classe de caractere de dígitos, que por sua vez é requerida pela ISO C para os caracteres de 0 a 9 e nada mais. Portanto, somente na localidade C , todos os [0-9] , [0123456789] , \d e [[:digit:]] significam exatamente o mesmo. O [0123456789] não tem interpretações erradas possíveis, [[:digit:]] está disponível em mais utilitários e é comum significar apenas [0123456789] . O \d é suportado por alguns utilitários.

Quanto a [0-9] , o significado de expressões de intervalo é definido apenas por POSIX na localidade C; em outras localidades, pode ser diferente (pode ser uma ordem codepoint ou ordem de intercalação ou outra coisa).

shells

Algumas implementações podem entender que um intervalo é algo diferente da ordem simples ASCII (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.

    
por 02.01.2018 / 04:44
12

Isso depende de como você define um dígito; [0-9] tende a ser apenas os ASCII (ou possivelmente algo que não seja nem ASCII nem um superconjunto de ASCII, mas os mesmos 10 dígitos que em ASCII apenas com representações de bit diferentes (EBCDIC)); \d , por outro lado, pode ser apenas os dígitos simples (versões antigas do Perl ou versões modernas do Perl com o sinalizador de expressão /a regular ativado) ou pode ser uma correspondência Unicode de \p{Digit} , que é conjunto maior de dígitos do que [0-9] ou /\d/a correspondê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 veja como se comporta.

Mas espere, tem mais! A localidade também pode variar o que \d corresponde, portanto, \d poderia corresponder a menos dígitos do que o conjunto Unicode completo de tal e (espero, geralmente) também inclui [0-9] . Isso é semelhante à diferença em C entre isdigit(3) ( [0-9] ) e isnumber(3) ( [0-9 plus qualquer outra coisa da localidade).

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
$ 
    
por 02.01.2018 / 04:42
4

Diferentes significados de [0-9] , [[:digit:]] e \d sã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 , \d depende. No manual do grep, é mencionado que [[:digit:]] é apenas 0-9 na C locale.

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

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

    
por 02.01.2018 / 14:45
3

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

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

Extração de dados por uma única vez

Muitas vezes, quando você quer compactar alguns números, os próprios números estão em um arquivo de texto formatado de forma desajeitada. Você quer extraí-los para uso em seu programa. Provavelmente você pode dizer o formato numérico (olhando para o arquivo) e sua localidade atual, então é ok usar qualquer um dos formulários , contanto que o trabalho seja feito. \d requer o menor número de pressionamentos de teclas, por isso é muito comumente usado.

Entrada de sanitização

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

Validação de dados

Você tem um pouco de dados que você não vai 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 de casa. Nesse caso, você provavelmente quer ser o mais amplo possível, então [[:digit:]] é o caminho a seguir.

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

    
por 03.01.2018 / 08:18