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


35

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

Quelles sont les circonstances où ils ne sont pas égaux? Quelle est la différence?

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


3
L' article Wikipedia que vous avez lié pour répondre à votre question Différents processeurs / moteurs d'expressions régulières prennent en charge différentes syntaxes pour les classes de caractères (entre autres).
igal

@igal wiki dit qu'il y a une différence mais ne donne pas beaucoup de détails. Je demande le détail, quelque chose comme isaac, thrig dit. Je suis assez intéressé par leur différence de grep, sed, awk ... que ce soit en version GNU ou non.
Harbinn

Réponses:


40

Oui, c’est [[:digit:]]~ [0-9]~ \d(où ~ signifie approximativement).
Dans la plupart des langages de programmation (où il est supporté) \d[[:digit:]](identique).
Le \dest moins courant que [[:digit:]](pas dans POSIX mais dans GNU grep -P).

Il y a beaucoup de chiffres dans UNICODE , par exemple:

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

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

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


Il existe de nombreux langages: Perl, Java, Python, C. Dans lequel [[:digit:]](et \d) appelle un sens étendu. 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 revient à sélectionner tous les caractères ayant les propriétés Unicode de Numericet digits:

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

Ce que grep pourrait reproduire (la version spécifique de pcre peut avoir une liste de points de code numérique différente de celle 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 les BRE POSIX ou ERE spécifiques:
Le \dn'est pas pris en charge (pas dans POSIX, mais dans GNU grep -P). [[:digit:]]POSIX exige que le code corresponde à la classe de caractères numériques, qui, à son tour, est requis par ISO C pour être les caractères de 0 à 9 et rien d’autre. Alors que dans C la locale tout [0-9], [0123456789], \det cela [[:digit:]]signifie exactement la même chose. Le [0123456789]a pas de mauvaise interprétation possible, [[:digit:]]est disponible dans plusieurs utilitaires et il est commun de ne signifier que [0123456789]. Le \dest pris en charge par quelques utilitaires.

Quant à [0-9]la signification des expressions de plage, elle n’est définie que par POSIX dans les paramètres régionaux C; dans d'autres pays, il peut être différent (par exemple, un ordre de codage, un ordre de classement ou autre chose).

coquilles

Certaines implémentations peuvent comprendre qu'une plage est quelque chose de différent de l'ordre ASCII ordinaire (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 en attente de réalisation.


En pratique sur les systèmes POSIX, iswctype()et BRE / ERE / les caractères génériques dans les utilitaires POSIX, [0-9] et [[: digit:]] ne correspondent que sur 0123456789. Et cela sera expliqué dans la prochaine révision de la norme
Stéphane Chazelas

Je ne savais pas que perlle \dmode Unicode correspondait aux chiffres décimaux d'autres scripts. Merci pour ça. Avec PCRE, voyez (*UCP)comme dans GNU grep -Po '(*UCP)\d'ou grep -Po '(*UCP)[[:digit:]]pour que les classes soient basées sur les propriétés Unicode.
Stéphane Chazelas

Je conviens que la [:digit:]syntaxe suggérerait que vous souhaitiez utiliser la localisation, c'est-à-dire tout ce que l'utilisateur considère comme un chiffre. Je ne l'utilise jamais [:digit:]car, dans la pratique, c'est la même chose [0-9]et, dans tous les cas, invariablement, je veux apparier sur 0123456789, je ne veux jamais faire correspondre ٠١٢٣٤٥٦٧٨٩, et je ne peux pas penser à un cas d'utilisation où l'on voudrait apparier sur un chiffre décimal. dans n'importe quel script avec les utilitaires POSIX. Voir aussi la discussion en cours [:blank:]sur le zsh ML . Ces classes de personnages sont un peu en désordre.
Stéphane Chazelas

13

Cela dépend de la manière dont vous définissez un chiffre. [0-9]tend à n'être que les ASCII (ou éventuellement quelque chose d'autre qui n'est ni un sur-ensemble d'ASCII mais les mêmes 10 chiffres que dans l'ASCII, mais avec des représentations de bits différentes (EBCDIC)); \dd'autre part pourrait être soit juste les chiffres simples (les anciennes versions de Perl, ou des versions modernes de Perl avec le /adrapeau d'expression régulière activée) ou il pourrait être un match Unicode \p{Digit}qui est plutôt un ensemble de chiffres plus que [0-9]ou /\d/acorrespondance.

$ 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 connaître son comportement.

Mais attendez, il y a plus! Les paramètres régionaux peuvent également varier en fonction des \dcorrespondances. Par conséquent, ils \dpourraient correspondre à moins de chiffres que l'ensemble complet Unicode de ce type, et (normalement, espérons-le) inclut également [0-9]. Ceci est similaire à la différence en C entre isdigit(3)( [0-9]) et isnumber(3)( [0-9plus tout ce qui vient des paramètres régionaux).

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

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

Je pense que isnumber()c’est un problème BSD, du moins sur la base de la page de manuel, il semblerait que oui
ilkkachu

J'ai un peu le parti pris de la BSD, oui
thrig le

Le drapeau / a est un limiteur spécifique permettant de réduire la liste des chiffres Unicode afin de ne faire correspondre que … le modificateur / a peut être utilisé pour forcer \ d à ne faire correspondre que le code ASCII 0 à 9 .. En tant que tel, il oblige à faire correspondre exactement le même et le seul [0-9].
Isaac

5

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

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

Donc ça [[:digit:]]marche toujours , ça \ddépend. Dans le manuel de grep, il est mentionné que [[:digit:]]c'est juste 0-9dans les Cparamètres régionaux.

PS1: Si vous en savez plus, développez le tableau.

PS2: GNU grep 3.1 et GNU 4.4 sont utilisés pour le test.


2
1) Il existe de nombreuses versions de grepet sed, avec la plus grande différence probablement entre les versions de GNU et les autres. Cette réponse pourrait être plus utile si elle mentionnait quelle version de grepet à laquelle sedelle fait référence. Ou quelle est la source de cette table, d'ailleurs. 2) cette table pourrait tout aussi bien être transcrite en texte, car elle ne contient rien qui la nécessite comme image
ilkkachu

@ilkkachu 1) les dernières versions de GNU grep 3.1 et GNU 4.4 sont utilisées pour le test. 2) Je ne sais pas comment créer un tableau. Il semble que @ muru a converti le tableau en un joli texte.
Harbinn le

@harbinn Veuillez l'éditer dans votre réponse.
Dan D.

@DanD. les informations de version ajoutées. thx pour l'attention
harbinn

1
Notez que le remodule intégré de python ne supporte pas [[: digit:]], mais que la bibliothèque add in le regexsupporte donc je voudrais un peu changer à la tâche habituelle. Cela fonctionne toujours dans des situations de plainte posix.
Steve Barnes

4

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 fois

Souvent, lorsque vous voulez modifier certains chiffres, ceux-ci se trouvent dans un fichier texte mal formaté. Vous voulez les extraire pour les utiliser dans votre programme. Vous pouvez probablement connaître le format numérique (en examinant le fichier) et vos paramètres régionaux actuels. Il est donc correct d'utiliser n'importe quel formulaire , à condition que le travail soit effectué. \dnécessite le moins de frappe possible, il est donc très utilisé.

Assainissement des entrées

Vous avez des entrées d'utilisateur non fiables (peut-être à partir d'un formulaire Web), et vous devez vous assurer qu'il ne contient pas de surprises. Peut-être souhaitez-vous le stocker dans un champ numérique d'une base de données ou l'utiliser comme paramètre d'une commande shell à 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 utiliser pour rien de "dangereux", mais il serait bon de savoir si c'est un nombre. Par exemple, votre programme permet à l'utilisateur de saisir une adresse et vous souhaitez mettre en évidence une faute de frappe possible si l'entrée ne contient pas de numéro de rue. Dans ce cas, vous voulez probablement être le plus large possible, [[:digit:]]le chemin à parcourir également.


Ceux-ci sembleraient être les trois cas d'utilisation les plus courants pour l'appariement de chiffres. Si vous pensez que j'ai raté une question importante, laissez s'il vous plaît un commentaire.


bon travail, Est-ce que le problème de sécurité est lié, tel que ReDoS ou autres
frams
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.