我应该使用 \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 正则表达式中的数字?的主要内容,如果未能解决你的问题,请参考以下文章