web-dev-qa-db-fra.com

Différence entre [0-9], [[: digit:]] et \ d

Dans l'article Wikipedia sur les expressions régulières , il semble que [[:digit:]] = [0-9] = \d.

Quelles sont les circonstances où elles ne sont pas égales? Quelle est la différence?

Après quelques recherches, je pense qu'une différence est que l'expression de parenthèse [:expr:] dépend des paramètres régionaux.

37
harbinn

Oui, ça l'est [[:digit:]] ~ [0-9] ~ \d (où ~ signifie approximatif).
Dans la plupart des langages de programmation (où il est pris en charge) \d[[:digit:]] (identique).
Le \d est moins courant que [[:digit:]] (pas dans POSIX mais il est dans GNU grep -P).

Il y a plusieurs chiffres dans UNICODE , par exemple:

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

Tous peut être inclus dans [[:digit:]] ou \d.

Au lieu, [0-9] n'est généralement que les ASCII chiffres 0123456789.


Il existe de nombreux langages: Perl, Java, Python, C. Dans lesquels [[:digit:]] (et \d) appelle une signification étendue. Par exemple, ce code Perl correspondra à tous les chiffres ci-dessus:

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

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

Ce qui équivaut à sélectionner tous les caractères qui ont les propriétés Unicode de Numeric et digits:

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

Quel grep pourrait reproduire (la version spécifique de pcre peut avoir une liste interne de points de code numérique différente de Perl):

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

Changez-le en [0-9] pour voir:

$ echo "$a" | grep -o '[0-9]\+'
0123456789
٠١٢٣٤٥٦٧٨
۰۱۲۳۴۵۶۷۸
߀߁߂߃߄߅߆߇߈
०१२३४५६७८

POSIX

Pour le POSIX BRE ou ERE spécifique:
Le \d n'est pas pris en charge (pas dans POSIX mais dans GNU grep -P). [[:digit:]] est requis par POSIX pour correspondre à la classe de caractères numériques, qui à son tour est requise par ISO C pour être les caractères 0 à 9 et rien d'autre. Donc uniquement dans les paramètres régionaux C tous [0-9], [0123456789], \d et [[:digit:]] signifie exactement la même chose. Le [0123456789] n'a pas d'interprétation erronée possible, [[:digit:]] est disponible dans plusieurs utilitaires et il est courant de vouloir dire seulement [0123456789]. Le \d est pris en charge par quelques utilitaires.

Pour ce qui est de [0-9], la signification des expressions de plage n'est définie que par POSIX dans les paramètres régionaux C; dans d'autres pays, cela peut être différent (peut être un ordre de point de code ou un ordre de classement ou autre).

coquilles

Certaines implémentations peuvent comprendre qu'une plage est quelque chose de différent de la simple ASCII order (ksh93 par exemple):

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

Et c'est une source sûre de bugs qui attendent de se produire.

42
Isaac

Cela dépend de la façon dont vous définissez un chiffre; [0-9] Tend à n'être que les ASCII ceux (ou peut-être autre chose qui n'est ni ASCII ni un surensemble de ASCII mais les 10 mêmes chiffres que dans ASCII uniquement avec des représentations binaires différentes (EBCDIC)); \d d'autre part pourrait être juste les chiffres simples (ancien des versions de Perl ou des versions modernes de Perl avec le drapeau d’expression régulière /a activé) ou il pourrait s’agir d’une correspondance Unicode de \p{Digit} qui est plutôt un ensemble de chiffres plus grand que [0-9] ou /\d/a correspondent.

$ 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 pour plus d'informations, ou consultez la documentation de la langue en question pour voir comment elle se comporte.

Mais attendez, il y a plus! Les paramètres régionaux peuvent également varier ce que \d Correspond, de sorte que \d Pourrait correspondre à moins de chiffres que l'ensemble Unicode de ce type, et (espérons-le, généralement) comprend également [0-9]. Ceci est similaire à la différence en C entre isdigit(3) ([0-9]) Et isnumber(3) ([0-9 Plus tout ce qui provient des paramètres régionaux).

Il peut y avoir des appels qui peuvent être effectués pour obtenir la valeur du chiffre, même si ce n'est pas [0-9]:

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

Signification différente de [0-9], [[:digit:]] et \d sont présentés dans d'autres réponses. Ici, je voudrais ajouter des différences dans la mise en œuvre du moteur regex.

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

Donc [[:digit:]] fonctionne toujours, \d dépend. Dans le manuel de grep, il est mentionné que [[:digit:]] est juste 0-9 dans les paramètres régionaux C.

PS1: Si vous en savez plus, veuillez agrandir le tableau.

PS2: GNU grep 3.1 et GNU 4.4 est utilisé pour le test).

7
harbinn

Les différences théoriques ont déjà été assez bien expliquées dans les autres réponses, il reste donc à expliquer les différences pratiques .

Voici certains des cas d'utilisation les plus courants pour faire correspondre un chiffre:


Extraction de données en une seule fois

Souvent, lorsque vous souhaitez réduire certains nombres, les nombres eux-mêmes se trouvent dans un fichier texte mal formaté. Vous souhaitez les extraire pour les utiliser dans votre programme. Vous pouvez probablement indiquer le format numérique (en regardant le fichier) et vos paramètres régionaux actuels, donc c'est ok pour utiliser n'importe lequel des formulaires, tant qu'il fait le travail. \d nécessite le moins de touches, donc il est très couramment utilisé.

Assainissement des entrées

Vous avez des entrées utilisateur non fiables (peut-être à partir d'un formulaire Web) et vous devez vous assurer qu'elles ne contiennent aucune surprise. Vous souhaitez peut-être le stocker dans un champ numérique d'une base de données ou l'utiliser comme paramètre d'une commande Shell pour l'exécuter sur un serveur. Dans ce cas, vous voulez vraiment [0-9], car c'est le plus restrictif et le plus prévisible.

La validation des données

Vous avez un peu de données que vous n'allez pas utiliser pour quelque chose de "dangereux", mais ce serait bien de savoir si c'est un nombre. Par exemple, votre programme permet à l'utilisateur d'entrer une adresse et vous souhaitez mettre en évidence une faute de frappe possible si l'entrée ne contient pas de numéro de maison. Dans ce cas, vous voulez probablement être aussi large que possible, donc [[:digit:]] est la voie à suivre.


Il semblerait que ce soient les trois cas d'utilisation les plus courants pour l'appariement des chiffres. Si vous pensez que j'en ai manqué un important, veuillez laisser un commentaire.

5
Bass