我应该使用\ d还是匹配Perl正则expression式中的数字?
在过去几个星期阅读了一些问题/答案之后,我发现在perl正则expression式中使用\d
是不正确的。 由于perl \d
的后面版本与[0-9]
,因为\d
将表示具有数字属性的任何Unicode字符,并且[0-9]
表示字符'0','1 ','2',…,'9'。
我明白,在某些情况下[0-9]
是正确的使用方法,而在其他情况下则是。 我想知道哪些人觉得是正确的默认使用?
就我个人而言,我发现\d
符号非常简洁和expression,而比较[0-9]
则有些麻烦。 但是我很less有多语言代码的经验,或者说编码不符合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
为了最大限度的安全,我build议你使用[0-9]
任何时候你不特别打算匹配所有的Unicode定义的数字。
根据perldoc perluniintro ,Perl不支持将数字[0-9]
以外的数字作为数字,所以如果以下两个条件都成立,我肯定会使用[0-9]
:
-
您希望将结果用作数字(例如对其执行math运算或将其存储在只接受正确数字的地方(例如数据库中的INT列))。
-
数据中可能有非数字
[^0-9]
,这样正则expression式可以匹配它们。 (请注意,对于不可信/恶意input,应始终考虑这一点。)
如果其中任何一个都是错误的,那么很less有理由明确不使用\d
(并且您可能会知道这是什么情况),如果您尝试匹配所有由unicode定义的数字,你一定要使用\d
。
在我看来,使用\d
是非常危险的,这是一个糟糕的devise决定,因为在大多数情况下,你想要[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
你还不相信吗?
根据perlreref ,' \d
'是区域意识和Unicode的意识。
但是,如果您使用的代码集不是Unicode,则不必担心Unicode数字,如果您使用的代码集类似于Latin-1(ISO 8859-1或8859-15),那么语言环境意识不会因为代码集不包含任何其他数字字符而伤害您。
所以,对于很多人来说,大部分时间,你可以使用' \d
'而不用担心。 但是,如果Unicode数据是您工作的一部分,那么您需要仔细考虑后再考虑一下。
就像从轨道上烧毁网站一样, [0-9]
是唯一可行的方法。 是的,这是丑陋的。 是的,select使\d
是UNICODE和区域意识是愚蠢的。 但这是我们的床,我们必须躺在床上。
至于那些在沙地上低头的人说它不会影响他们今天使用的字符集,那么今天你可能会使用这个字符集,但是现在世界其他地方正在使用UTF-8,你将会也很快使用它。 请记住像维护你的代码的人是一个知道你住在哪里的杀人狂。
呵呵,对于使用\d
和[0-9]
Perl模块,即使核心仍然存在UNICODE问题 。
如果你实际上是指任何数字,但是希望能够用结果做math运算,你可以使用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";
经过一些更多的testing,看起来像Text :: Unidecode不能正确处理所有的数字字符。 我正在写一个模块 ,将工作。
我觉得都必须有自己的位置。 然而,99.999%的时间(特别是在我封闭的美国大合作世界),它们是可以互换的。 我每天都使用perl来操作数据,在我处理的数据集中没有一个数字不适合[0-9]
。 不过,我很欣赏\d
和[0-9]
之间有一个重要的区别,很好地意识到这个区别。 我使用\d
因为它看起来更简洁(正如你所说的),并且在我的小数据操作世界里永远不会是“错误的”。
如果将\d
应用于Unicodestring(例如"\X{660}" =~ /\d/
),则会匹配一个Unicode数字。 如果将\d
应用于二进制string(例如上面的UTF-8等效"\xd9\xa0" =~ /\d/
),它将只匹配10个ASCII数字。 Perl 5.8默认不创buildUnicodestring(除非你特别要求它,比如在"\X{...}"
或者use utf8;
等)。
所以我的build议是:如果您的应用程序使用Unicodestring,请注意\d
和[0-9]
之间的区别。
如果[0-9]
觉得笨重,也许你可以定义: $d=qr/[0-9]/;
并使用它来代替\d
。
随着数据格式控制的增加,对模式特异性的需求也随之下降。
例如,如果您匹配的是机器生成的一段数据,并始终遵循相同的输出格式规则,则不需要如此精确。 拿IPv4地址。 如果您试图从路由器接口configuration行中提取IP地址,那么您真正需要的就是:
'ip\haddress\h(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\D'
另一方面,如果你正试图find一个embedded在某个电子邮件X-Header中的IP地址,或者如果你想validation一个IP地址,那么这是一个完整的“另一个故事!