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

时间:2009-05-20 23:09:14

标签: regex perl

在过去几周阅读了一些问题/答案后,我看到在perl正则表达式中使用\d被评论为不正确。与更高版本的perl \d[0-9]不同,因为\d将表示具有digit属性的任何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

8 个答案:

答案 0 :(得分:53)

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

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

  • 如果我正确阅读了unicode图表,那么'۷۰'是一个数字(70 in in indic,请不要相信我的话)。

  • 试试这个:

    $ 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 :(得分:38)

为了最大限度地提高安全性,我建议您在没有特别打算匹配所有unicode定义的数字时使用[0-9]

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

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

  2. 数据中可能存在非数字[^0-9],正则表达式可以匹配它们。 (请注意,对于不受信任/恶意的输入,此始终应被视为true。)

  3. 如果其中任何一个都是假的,那么很少有理由使用\d(并且您可能能够判断何时是这种情况),并且如果您尝试匹配所有unicode定义的数字,那么您肯定希望使用\d

答案 2 :(得分:8)

根据perlreref,“\d”具有区域设置感知和Unicode识别功能。

但是,如果您使用的代码集不是Unicode,那么您不必担心Unicode数字,如果您使用的代码集类似于Latin-1(ISO 8859-1或8859-) 15),那么语言环境感知不会对你造成伤害,因为代码集不包含任何其他数字字符。

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

答案 3 :(得分:5)

就像从轨道上钻取网站一样,[0-9]是确保唯一的方法。是的,它很难看。是的,让\d成为UNICODE和语言环境的选择是愚蠢的。但这是我们的床,我们必须躺在床上。

至于人们在沙滩上低头说它不会影响他们今天使用的角色,你今天可能正在使用这个角色,但世界其他地方现在正在使用UTF-8你很快就会使用它。记住代码就像那个维护你的代码的人是一个杀人的疯子,他知道你住在哪里。

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

如果你的确意味着任何数字,但希望能够对结果进行数学计算,你可以使用Text::Unidecode

#!/usr/bin/perl

use strict;
use warnings;

use Text::Unidecode;

my $number = "\x{1811}\x{1812}\x{1813}\x{1814}\x{1815}";
print "$number is ", unidecode($number), "\n";

经过一些测试后,看起来Text :: Unidecode无法正确处理所有数字字符。我正在编写一个可行的module

答案 4 :(得分:3)

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

答案 5 :(得分:2)

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

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

答案 6 :(得分:1)

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

答案 7 :(得分:0)

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

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

 'ip\haddress\h(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\D'

另一方面,如果您正在尝试查找嵌入在电子邮件X-Header中某处的IP地址,或者如果您尝试验证IP地址,那么这就是整个& #39;其他故事!