我应该使用 \d 还是 [0-9] 来匹配 Perl 正则表达式中的数字?

Posted

技术标签:

【中文标题】我应该使用 \\d 还是 [0-9] 来匹配 Perl 正则表达式中的数字?【英文标题】:Should I use \d or [0-9] to match digits in a Perl regex?我应该使用 \d 还是 [0-9] 来匹配 Perl 正则表达式中的数字? 【发布时间】:2010-10-27 19:15:06 【问题描述】:

在过去几周阅读了许多问题/答案后,我看到在 perl 正则表达式中使用 \d 被评论为不正确。在 perl 的更高版本中\d[0-9] 不同,因为\d 将代表任何具有数字属性的Unicode 字符,而[0-9] 代表字符'0'、'1'、 '2', ..., '9'。

我很欣赏在某些情况下[0-9] 将是正确的使用方式,而在其他情况下\d 将是正确的。我想知道哪些人认为使用正确的默认值?

我个人觉得\d 表示法非常简洁和富有表现力,而相比之下[0-9] 则有些麻烦。但我很少有编写多语言代码的经验,或者更确切地说是为不适合 ASCII 字符范围的语言编写代码,因此可能是幼稚的。

我注意到了

$find /System/Library/Perl/5.8.8/ -name \*pm | xargs grep '\\d' | wc -l
  298
$find /System/Library/Perl/5.8.8/ -name \*pm | xargs grep '\[0-9\]' | wc -l
  26

【问题讨论】:

【参考方案1】:

在我看来,使用\d 非常危险,在语言中这是一个糟糕的设计决策,因为在大多数情况下你想要[0-9]。霍夫曼编码将规定使用\d 表示ASCII 数字。

之前的大多数海报已经强调了为什么你应该使用[0-9],所以让我再给你一点数据:

如果我正确阅读了 unicode 图表,“۷۰”是一个数字(印度语中的 70,不要相信我的话)。

试试这个:

$ perl -le '$one = chr 0xFF11; print "$one + 1 = ", $one+1;'
1 + 1 = 1

这里是有效数字的部分列表(可能会或可能不会在浏览器中正确显示,具体取决于您使用的字体),对于每个数字,只有第一个在进行算术运算时被解释为数字Perl,如上图:

 ZERO:  0٠۰߀०০੦૦୦௦౦೦൦๐໐0
 ONE:   1١۱߁१১੧૧୧௧౧೧൧๑໑1
 TWO:   2٢۲߂२২੨૨୨௨౨೨൨๒໒2
 THREE: 3٣۳߃३৩੩૩୩௩౩೩൩๓໓3
 FOUR:  4٤۴߄४৪੪૪୪௪౪೪൪๔໔4
 FIVE:  5٥۵߅५৫੫૫୫௫౫೫൫๕໕5
 SIX:   6٦۶߆६৬੬૬୬௬౬೬൬๖໖6
 SEVEN: 7٧۷߇७৭੭૭୭௭౭೭൭๗໗7
 EIGHT: 8٨۸߈८৮੮૮୮௮౮೮൮๘໘8
 NINE:  9٩۹߉९৯੯૯୯௯౯೯൯๙໙9��

你还不服气吗?

【讨论】:

+1 为该列表!我开始想知道还有哪些其他数字字符。 如果 Perl 到目前为止已经接受了 UNICODE,那么它似乎应该继续处理所有的数字。当然,这种方式是疯狂的,但疯狂不是所有 Perl 程序员的命运 ;-) 吗? 还有更多字符,但我只包括了我可以在我的系统上显示的字符。我使用了来自unicode.org/Public/UNIDATA/UnicodeData.txt 的 unicode 数据,并从那里提取了字符信息。 @nickf 在我目前的计数中,有 61 组数字,请参阅我的答案中的模块链接以获取列表。 @Beano 我不是说不要使用 \d;我是说当你的意思是 [0-9] 时不要使用 \d。当您的意思是 [ ] 时,它类似于不使用 \s。问题归结为你介意匹配⑤和5吗?【参考方案2】:

为了最大程度的安全,我建议您在任何时候不打算匹配所有 unicode 定义的数字时使用 [0-9]

根据 perldoc perluniintro,Perl 不支持使用除 [0-9] 以外的数字作为数字,所以如果以下都为真,我肯定会使用 [0-9]

    您希望将结果用作数字(例如对其执行数学运算或将其存储在仅接受正确数字的位置(例如数据库中的 INT 列))。

    非数字 [^0-9] 可能会以正则表达式匹配它们的方式出现在数据中。 (请注意,对于不受信任/敌对的输入,这应该始终被认为是正确的。)

如果其中任何一个是错误的,那么很少有理由专门使用\d(你可能会知道什么时候是这种情况),如果你're 尝试匹配所有 unicode 定义的数字,你肯定想使用\d

【讨论】:

\d 如果应用于 Unicode 字符串,确实可以匹配超过 10 个不同的字符。【参考方案3】:

根据perlreref,“\d”可以识别区域设置和 Unicode。

但是,如果您使用的代码集不是 Unicode,那么您无需担心 Unicode 数字,并且如果您使用的代码集类似于 Latin-1(ISO 8859-1 或 8859- 15),那么语言环境意识也不会伤害您,因为代码集不包含任何其他数字字符。

因此,对于很多人来说,大多数时候,您可以毫无顾虑地使用“\d”。但是,如果 Unicode 数据是您工作的一部分,那么您需要更仔细地考虑您的目标。

【讨论】:

【参考方案4】:

就像从轨道上攻击站点一样,[0-9] 是唯一可以确定的方法。是的,它很丑。是的,让\d 成为 UNICODE 和区域设置的选择是愚蠢的。但这是我们的床,我们必须躺在上面。

至于那些低头在沙子里说这不会影响他们今天使用的字符集的人,你今天可能正在使用那个字符集,但是世界其他地方现在正在使用 UTF-8 并且您也将很快使用它。记住要像维护代码的人一样编写代码,他是一个知道你住在哪里的杀人狂。

哦,对于使用\d[0-9] 的Perl 模块,即使核心仍然有UNICODE problems。

如果您实际上是指任何数字,但希望能够对结果进行数学运算,您可以使用Text::Unidecode

#!/usr/bin/perl

use strict;
use warnings;

use Text::Unidecode;

my $number = "\x1811\x1812\x1813\x1814\x1815";
print "$number is ", unidecode($number), "\n";

经过更多测试,看起来 Text::Unidecode 无法正确处理所有数字字符。我正在写一个module,它会起作用。

【讨论】:

【参考方案5】:

如果您将\d 应用于Unicode 字符串(例如"\X660" =~ /\d/),它将匹配Unicode 数字。如果将\d 应用于二进制字符串(例如上面的UTF-8 等效项:"\xd9\xa0" =~ /\d/),它将仅匹配10 个ASCII 数字。 Perl 5.8 默认不创建 Unicode 字符串(除非您特别要求,例如在 "\X..."use utf8; 等中)。

所以我的建议是:如果您的应用程序使用 Unicode 字符串,请仅注意 \d[0-9] 之间的区别。

【讨论】:

【参考方案6】:

我觉得两者都必须有自己的位置。但是,99.999% 的时间(尤其是在我封闭的美国大合作世界中)它们是可以互换的。我每天都使用 perl 来处理数据,在我处理的所有数据集中都没有不适合 [0-9] 的数字。但是,我确实很欣赏\d[0-9] 之间的重要区别,很高兴了解这种区别。我使用 \d 是因为它看起来更简洁(如您所说),并且在我的数据操作小世界中永远不会“错误”。

【讨论】:

你想要 \d 而不是 /d - 如果你想要的话。【参考方案7】:

如果[0-9] 感觉笨拙,也许您可​​以定义:$d=qr/[0-9]/; 并使用它来代替\d

【讨论】:

【参考方案8】:

随着数据格式控制的增加,对模式特异性的需求下降......

例如,如果您要匹配的数据是机器生成的,并且始终遵循相同的输出格式规则,则无需如此精确。 获取 IPv4 地址。如果您尝试从路由器接口配置行中提取 IP 地址,您真正需要的是:

 'ip\haddress\h(\d1,3\.\d1,3\.\d1,3\.\d1,3)\D'

另一方面,如果您正试图找到一个嵌入在某处深处的 IP 地址,例如,电子邮件 X-Header,或者如果您正试图验证一个 IP 地址,那么……这是一个完整的 '另一个故事!

【讨论】:

以上是关于我应该使用 \d 还是 [0-9] 来匹配 Perl 正则表达式中的数字?的主要内容,如果未能解决你的问题,请参考以下文章

d只匹配0-9位数?

常用的正则匹配表达式

正则表达式收集

常用正则表达式

正则表达式大全,随用随查

正则表达式