答案1
是的,它是[[:digit:]]
~~ (其中〜表示近似)。 在大多数编程语言(支持的语言)中[0-9]
\d
\d ≡ `[[:digit:]]` # (is identical to, it is a short hand for).
存在\d
的实例少于[[:digit:]]
(在 POSIX 中可用grep -P
,但在 POSIX 中不可用)。
统一码数字
有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 语言环境中所有[0-9]
、[0123456789]
、\d
和[[: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 ٠١٢٣٤٥٦٧٨٩ ۰۱۲۳۴۵۶۷۸۹ ߀߁߂߃߄߅߆߇߈߉ ०१२३४५६७८९'
尝试grep
发现它允许大多数:
$ echo "$str" | grep -o '[0-9]\+'
0123456789
٠١٢٣٤٥٦٧٨
۰۱۲۳۴۵۶۷۸
߀߁߂߃߄߅߆߇߈
०१२३४५६७८
那个 sed 有一些麻烦。应该仅删除0123456789
但删除几乎所有数字。这意味着它接受大多数数字,但不接受一些九(???):
$ echo "$str" | sed 's/[0-9]\{1,\}//g'
٩ ۹ ߉ ९
即使 expr 也遇到了与 sed 相同的问题:
expr "$str" : '\([0-9 ]*\)' # also matching spaces.
0123456789 ٠١٢٣٤٥٦٧٨
还有编辑
printf '%s\n' 's/[0-9]/x/g' '1,p' Q | ed -v <(echo "$str")
105
xxxxxxxxxx xxxxxxxxx٩ xxxxxxxxx۹ xxxxxxxxx߉ xxxxxxxxx९
[[:数字:]]
语言有很多种:Perl、Java、Python、C。其中[[:digit:]]
(和\d
) 需要扩展含义。例如,此 perl 代码将匹配上面的所有数字:
$ str='0123456789 ٠١٢٣٤٥٦٧٨٩ ۰۱۲۳۴۵۶۷۸۹ ߀߁߂߃߄߅߆߇߈߉ ०१२३४५६७८९'
$ echo "$str" | perl -C -pe 's/[^\d]//g;' ; echo
0123456789٠١٢٣٤٥٦٧٨٩۰۱۲۳۴۵۶۷۸۹߀߁߂߃߄߅߆߇߈߉०१२३४५६७८९
这相当于选择具有Numeric
和的 Unicode 属性的所有字符digits
:
$ echo "$str" | perl -C -pe 's/[^\p{Nd}]//g;' ; echo
0123456789٠١٢٣٤٥٦٧٨٩۰۱۲۳۴۵۶۷۸۹߀߁߂߃߄߅߆߇߈߉०१२३४५६७८९
哪个 grep 可以重现(特定版本的 pcre 可能具有与 Perl 不同的内部数字代码点列表):
$ echo "$str" | grep -oP '\p{Nd}+'
0123456789
٠١٢٣٤٥٦٧٨٩
۰۱۲۳۴۵۶۷۸۹
߀߁߂߃߄߅߆߇߈߉
०१२३४५६७८९
贝壳
某些实现可能会将范围理解为与普通 ASCII 顺序不同的内容(例如 ksh93)(在 2018 年 5 月版本 (AT&T Research) 93u+ 2012-08-01 上进行测试时):
$ LC_ALL=en_US.utf8 ksh -c 'echo "${1//[0-9]}"' sh "$str"
۹ ߀߁߂߃߄߅߆߇߈߉ ९
现在(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"
٩ ۹ ߉ ९
在我看来,这肯定是即将发生的错误的来源。
答案2
这取决于您如何定义数字;[0-9]
往往只是 ASCII 数字(或者可能是其他既不是 ASCII 也不是 ASCII 超集的数字,而是与 ASCII 中相同的 10 位数字,只是具有不同的位表示形式 (EBCDIC));\d
另一方面,它可能只是简单的数字(旧版本的 Perl,或/a
启用了正则表达式标志的现代版本的 Perl),也可能是 Unicode 匹配,其中的数字集比或匹配\p{Digit}
更大。[0-9]
/\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]
.这类似于 C 中isdigit(3)
( [0-9]
) 和isnumber(3)
([0-9
以及语言环境中的其他内容) 之间的差异。
可能可以进行调用来获取该数字的值,即使它不是[0-9]
:
$ perl -MUnicode::UCD=num -E 'say num(4)'
4
$ perl -MUnicode::UCD=num -E 'say num("\N{U+09EA}")'
4
$
答案3
其他答案中已经很好地解释了理论上的差异,因此仍然需要解释实际的差异。
以下是匹配数字的一些更常见的用例:
一次性数据提取
通常,当您想要处理一些数字时,数字本身位于格式笨拙的文本文件中。您想要提取它们以在您的程序中使用。您可能可以知道数字格式(通过查看文件)和您当前的区域设置,所以它是可以使用任何表格,只要能完成工作。\d
需要最少的击键,因此非常常用。
输入清理
您有一些不受信任的用户输入(可能来自网络表单),并且您需要确保它不包含任何意外。也许您想将其存储在数据库的数字字段中,或者用作在服务器上运行的 shell 命令的参数。在这种情况下,你真的想要[0-9]
,因为它是最具限制性和可预测性的。
数据验证
您有一些数据,您不会将其用于任何“危险”的事情,但很高兴知道它是否是一个数字。例如,您的程序允许用户输入地址,并且如果输入不包含门牌号,您希望突出显示可能的拼写错误。在这种情况下,您可能希望尽可能广泛,所以[[:digit:]]
是要走的路。
这些似乎是数字匹配的三个最常见的用例。如果您认为我错过了重要的内容,请发表评论。
答案4
和[0-9]
的不同含义在其他答案中有介绍。在这里我想添加正则表达式引擎的实现差异。[[:digit:]]
\d
[[:digit:]] \d
grep -E ✓ ×
grep -P ✓ ✓
sed ✓ ×
sed -E ✓ ×
所以[[:digit:]]
总是有效,\d
依靠。在 grep 的手册中提到它[[:digit:]]
只是0-9
在C
语言环境中。
PS1:如果您了解更多,请扩展表格。
PS2:使用GNU grep 3.1和GNU 4.4进行测试。