[0-9], [[:digit:]] 和 \d 之间的区别

har*_*inn 58 regular-expression wildcards

正则表达式维基百科的文章,似乎[[:digit:]]= [0-9]= \d

它们不相等的情况是什么?有什么不同?

经过一些研究,我认为一个区别是括号表达式[:expr:]依赖于语言环境。

ImH*_*ere 67

是的,它是[[:digit:]]~ [0-9]~ \d(其中 ~ 表示近似)。
在大多数编程语言中(支持的地方)

\d ? `[[:digit:]]`            # (is identical to, it is a short hand for).  
Run Code Online (Sandbox Code Playgroud)

\d在小于情况存在[[:digit:]](可用grep -P,但不是在POSIX)。

Unicode 数字

UNICODE 中很多数字,例如:

123456789 # Hindu-Arabic 阿拉伯数字
?????????? # ARABIC-INDIC
?????????? # EXTENDED ARABIC-INDIC/PERSIAN
?????????? # NKO DIGIT
?????????? # DEVANAGARI

所有这些都可能包含[[:digit:]]或 中\d,甚至有些情况下[0-9]


POSIX

对于具体POSIX BRE或ERE:
\d不支持(不属于POSIX但在GNU grep -P)。 [[:digit:]]POSIX 要求与数字字符类相对应,而 ISO C 要求它是字符 0 到 9 而不是其他字符。所以只有在 C 语言环境中all [0-9], [0123456789], \dand 的[[:digit:]]意思完全一样。在[0123456789]没有可能的误解,[[:digit:]]是更多的实用程序可用,在某些情况下,只意味着[0123456789]。该\d由几个实用程序支持。

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

[0123456789]

所有 ASCII 数字的最基本选项。
始终有效,(AFAICT)没有已知的失败实例。

它只匹配英文数字:0123456789

[0-9]

一般认为[0-9]只是ASCII数字0123456789
在某些情况下,这是非常错误的:Linux 在某些语言环境中不是“C”(2020 年 6 月)系统,例如:

认为:

str='0123456789 ?????????? ?????????? ?????????? ??????????'
Run Code Online (Sandbox Code Playgroud)

尝试grep发现它允许大多数:

$ echo "$str" | grep -o '[0-9]\+'
0123456789
?????????
?????????
?????????
?????????
Run Code Online (Sandbox Code Playgroud)

那个 sed 有一些麻烦。应该只删除0123456789但删除几乎所有数字。这意味着它接受大多数数字,但不接受一些 9 (???):

$ echo "$str" | sed 's/[0-9]\{1,\}//g'
 ? ? ? ?
Run Code Online (Sandbox Code Playgroud)

甚至 expr 也遭受同样的 sed 问题:

expr "$str" : '\([0-9 ]*\)'             # also matching spaces.
0123456789 ?????????
Run Code Online (Sandbox Code Playgroud)

还有 ed

printf '%s\n' 's/[0-9]/x/g' '1,p' Q | ed -v <(echo "$str")
105
xxxxxxxxxx xxxxxxxxx? xxxxxxxxx? xxxxxxxxx? xxxxxxxxx?
Run Code Online (Sandbox Code Playgroud)

[[:数字:]]

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

$ str='0123456789 ?????????? ?????????? ?????????? ??????????'

$ echo "$str" | perl -C -pe 's/[^\d]//g;' ; echo
0123456789????????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)

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

$ echo "$str" | perl -C -pe 's/[^\p{Nd}]//g;' ; echo
0123456789????????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)

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

$ echo "$str" | grep -oP '\p{Nd}+'
0123456789
??????????
??????????
??????????
??????????
Run Code Online (Sandbox Code Playgroud)

贝壳

某些实现可能会将范围理解为不同于纯 ASCII 顺序(例如 ksh93)(在 2018 年 5 月版本(AT&T Research)93u+ 2012-08-01 上测试时):

$ LC_ALL=en_US.utf8 ksh -c 'echo "${1//[0-9]}"' sh "$str"
  ? ?????????? ?
Run Code Online (Sandbox Code Playgroud)

现在(2020 年 6 月),来自 debian 的相同软件包 ksh93(相同版本 sh (AT&T Research) 93u+ 2012-08-01):

$ LC_ALL=en_US.utf8 ksh -c 'echo "${1//[0-9]}"' sh "$str"

 ? ? ? ?
Run Code Online (Sandbox Code Playgroud)

在我看来,这是等待发生的错误的确定来源。

  • 回复我上面 2 年前的评论,很大程度上要感谢 Isaac 在这里的发现,我现在不再使用 `[0-9]`(除了在 zsh/perl 或我知道可以按预期工作的 C 语言环境中)作为什么匹配或多或少是随机的,在 POSIX 实用程序中使用 `[[:digit:]]`,或者在我不能确定时使用 `[0123456789]`。`[az]` 的情况更糟。 (4认同)

thr*_*rig 16

这取决于您如何定义数字;[0-9]往往只是 ASCII 的(或者可能是其他既不是 ASCII 也不是 ASCII 超集的东西,而是与 ASCII 相同的 10 位数字,只是具有不同的位表示(EBCDIC));\d另一方面,可以只是纯数字(旧版本的 Perl,或/a启用了正则表达式标志的现代版本的 Perl ),也可以是 Unicode 匹配,\p{Digit}其中的数字集比[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]/'
$ 
Run Code Online (Sandbox Code Playgroud)

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
$ 
Run Code Online (Sandbox Code Playgroud)


har*_*inn 7

[0-9],[[:digit:]]和 的不同含义\d在其他答案中有所介绍。在这里,我想添加正则表达式引擎实现的差异。

            [[:digit:]]    \d
grep -E               ?     ×
grep -P               ?     ?
sed                   ?     ×
sed -E                ?     ×
Run Code Online (Sandbox Code Playgroud)

所以[[:digit:]]总是有效\d取决于。在 grep 的手册中提到它[[:digit:]]只是0-9C语言环境中。

PS1:如果你知道更多,请扩大表格。

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

  • 1) `grep` 和 `sed` 有很多版本,最大的区别可能是 GNU 版本与其他版本。如果这个答案提到它指的是哪个版本的 `grep` 和 `sed`,它可能会更有用。或者那个表的来源是什么,就此而言。2)该表格也可以转录为文本,因为它不包含任何需要它是图像的内容 (2认同)

Bas*_*ass 6

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

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


一键式数据提取

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

输入消毒

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

数据验证

您有一些数据不会用于任何“危险”的事情,但很高兴知道它是否是一个数字。例如,您的程序允许用户输入地址,如果输入不包含门牌号,您希望突出显示可能的拼写错误。在这种情况下,您可能希望尽可能广泛,所以[[:digit:]]要走的路。


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