[0-9],[[:digit:]]和\ d之间的差异


35

Wikipedia关于正则表达式的文章中,似乎[[:digit:]]= [0-9]= \d

在什么情况下它们不相等?有什么不同?

经过一番研究,我认为其中的一个区别是方括号表达[:expr:]是区域性的。


3
您链接的Wikipedia文章不是回答您的问题吗?不同的正则表达式处理器/引擎支持字符类的不同语法(除其他外)。
igal

@igal Wiki说有区别,但没有提供太多细节。索格说,我想问的是细节,比如像以撒。我对它们在grep,sed,awk等方面的差异非常感兴趣,无论是否为GNU版本。
哈尔滨

Answers:


40

是的,这是[[:digit:]][0-9]\d(这里〜手段aproximate)。
在大多数的编程语言(它支持)\d[[:digit:]](相同)。
\d相比[[:digit:]],它不那么常见(不是在POSIX中,而是在GNU中grep -P)。

UNICODE中很多数字,例如:

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

所有这些都可以包含[[:digit:]]或中\d

相反,[0-9]通常只有ASCII数位0123456789


有许多语言:Perl,Java,Python和C。其中[[:digit:]](和\d)要求扩展的含义。例如,此Perl代码将匹配上面的所有数字:

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

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

这相当于选择具有的Unicode属性所有字符Numericdigits

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

哪个grep可以复制(特定版本的pcre可能与Perl相比具有不同的内部数字代码点列表):

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

将其更改为[0-9]以查看:

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

POSIX

对于具体POSIX BRE或ERE:
\d不支持(不属于POSIX但在GNU grep -P)。 [[:digit:]]POSIX要求将其与数字字符类相对应,而数字字符类又由ISO C要求是字符0到9,而没有其他字符。因此,只有在C语言环境所有[0-9][0123456789]\d[[:digit:]]平均一模一样。在[0123456789]没有可能的误解,[[:digit:]]是更多的实用程序可用,但通常仅意味[0123456789]。该\d由几个实用程序支持。

至于[0-9]范围表达式的含义仅由C语言环境中的POSIX定义; 在其他语言环境中,它可能有所不同(可能是代码点顺序或排序规则顺序或其他)。

炮弹

一些实现可能将范围理解为与纯ASCII顺序(例如ksh93)不同:

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

这肯定是等待发生错误的来源。


实际上,在POSIX系统iswctype()和POSIX实用程序中的BRE / ERE /通配符上,[0-9]和[[:digit:]]仅与0123456789匹配。而将要进行标准的下一版本明确
斯特凡Chazelas

我不知道perl\d关于从其他脚本十进制数字匹配Unicode模式。感谢那。对于PCRE,请参见(*UCP)GNU中的内容,grep -Po '(*UCP)\d'或者grep -Po '(*UCP)[[:digit:]]使类基于Unicode属性。
斯特凡Chazelas

我同意该[:digit:]语法建议您使用本地化,即用户认为是数字的任何内容。我从不使用,[:digit:]因为在实践[0-9]中无论如何我总是要匹配0123456789,在任何情况下我都想匹配,我从不打算匹配٠١٢٣٤٥٦٧٨٩,并且我想不出一个用例要匹配十进制数字的用例在任何带有POSIX实用程序的脚本中。另请参阅有关[:blank:]zsh ML当前讨论。这些字符类有点混乱。
斯特凡Chazelas

13

这取决于您如何定义数字。[0-9]往往只是ASCII码(或其他可能既不是ASCII也不是ASCII的超集,但与ASCII相同的10位数字,只是具有不同的位表示形式(EBCDIC));\d另一方面,可以是纯数字(Perl的旧版本,或者/a启用了正则表达式标志的Perl的现代版本),也可以是Unicode匹配,\p{Digit}而Unicode匹配则比[0-9]or 更大/\d/a

$ 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 有关更多信息,或查阅有关语言的文档以了解其行为。

但是,等等,还有更多!语言环境也可能会改变\d匹配的内容,因此\d匹配的位数可能少于此类的完整Unicode集,并且(希望通常是)还包括[0-9]。这类似于isdigit(3)[0-9])和isnumber(3)[0-9再加上语言环境中的其他内容)之间C的差异。

可能会进行调用以获取数字的值,即使不是[0-9]

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

我认为这isnumber()是BSD的事情,至少基于手册页看来如此
ilkkachu

我确实有某种BSD偏见,是的
快感

/ a标志是一个特定的限制器,用于减少Unicode数字列表以仅匹配…... / a修饰符可用于强制\ d仅匹配ASCII 0到9。因此,它强制匹配完全相同且唯一的[0-9]
艾萨克(Isaac)

5

不同的意义的[0-9][[:digit:]]\d在其他的答案被呈现。在这里,我想补充一下正则表达式引擎实现的差异。

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

因此[[:digit:]]始终有效\d取决于。在grep的手册中,提到[[:digit:]]的只是0-9C语言环境中。

PS1:如果您了解更多信息,请扩展表格。

PS2:GNU grep 3.1和GNU 4.4用于测试。


2
1)grep和的版本很多sed,GNU版本与其他版本之间的最大差异可能很大。如果它提到的是哪个版本这个答案可能会更有用grepsed它是指。或者,该表的来源是什么。2)该表格也可能会转录为文本,因为它不包含任何要求将其作为图像的内容
ilkkachu

@ilkkachu 1)使用最新的GNU grep 3.1和GNU 4.4进行测试。2)我不怎么创建表。看来@ muru已将表格转换为漂亮的文本形式。
哈尔滨

@harbinn请编辑为您的答案。
Dan D.

@丹 添加的版本信息。值得关注的东西
harbinn

1
请注意,python内置re模块不支持[[:digit:]],但是add in库regex确实支持它,所以我总是会有点儿麻烦。它始终适用于posix投诉情况。
史蒂夫·巴恩斯

4

在其他答案中已经很好地解释了理论差异,因此仍然需要解释实际差异。

以下是一些与数字匹配的较常见用例:


一站式数据提取

通常,当您想处理一些数字时,数字本身位于格式笨拙的文本文件中。您要提取它们以在程序中使用。您可能可以告诉数字格式(通过查看文件)和当前的语言环境,因此可以使用任何形式,只要可以完成工作即可。\d需要最少的击键,因此非常常用。

输入消毒

您有一些不受信任的用户输入(也许来自Web表单),并且需要确保它不包含任何意外。也许您想将其存储在数据库的数字字段中,或者用作外壳程序命令的参数以在服务器上运行。在这种情况下,您确实想要[0-9],因为它是最严格和最可预测的。

资料验证

您有一些数据不会用于“危险”的事情,但是很高兴知道它是否为数字。例如,您的程序允许用户输入地址,并且如果输入的内容不包含门牌号,则希望突出显示可能的错字。在这种情况下,您可能希望尽可能地广泛,所以[[:digit:]]方法也应如此。


这些似乎是数字匹配的三个最常见的用例。如果您认为我错过了重要的一个,请发表评论。


干得好,是否与安全问题相关,例如ReDoS或其他问题
frams
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.