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

3ks5zfa0  于 2022-12-19  发布在  Perl
关注(0)|答案(9)|浏览(200)

在过去的几个星期里,我读了很多问题/答案,我看到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
w6lpcovy

w6lpcovy1#

在我看来,使用\d是非常危险的,这是一个糟糕的语言设计决策,因为在大多数情况下,你需要[0-9]。霍夫曼编码将规定使用\d的ASCII数字。
前面的大多数海报已经强调了为什么应该使用[0-9],所以让我再给予你一些数据:

  • 如果我正确地阅读了unicode图表,“۷۰”是一个数字(在indic中是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��

你还不相信吗?

brgchamk

brgchamk2#

为了最大限度的安全,我建议在您不特别打算匹配所有Unicode定义的数字时使用[0-9]
根据perldoc perluniintro,Perl不支持使用[0-9]以外的数字作为数字,因此如果以下两个条件都为真,我肯定会使用[0-9]
1.您希望将结果用作数字(例如对其执行数学运算或将其存储在只接受正确数字的地方(例如数据库中的INT列))。
1.数据中可能存在非数字[^0-9],正则表达式可能会匹配它们(注意,对于不可信/恶意输入,这个表达式 * 总是 * 被视为true)。
如果其中一个为false,那么只有很少的情况下才有理由明确地“不”使用\d(并且您可能能够判断出什么时候是这种情况),如果您"尝试“匹配所有Unicode定义的数字,那么您肯定希望使用\d

vsaztqbk

vsaztqbk3#

根据perlreref,\d可以识别区域设置和Unicode。
但是,如果您使用的代码集不是Unicode,那么您就不必担心Unicode数字,如果您使用的代码集类似于Latin-1(ISO 8859-1或8859-15),那么区域设置意识也不会对您造成伤害,因为代码集不包含任何其他数字字符。
因此,对于许多人来说,在大多数情况下,您可以毫无顾虑地使用\d,但是,如果Unicode数据是您工作的一部分,那么您需要更仔细地考虑您所追求的是什么。

gcxthw6b

gcxthw6b4#

就像从轨道上用核弹摧毁网站一样,[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 = "\x{1811}\x{1812}\x{1813}\x{1814}\x{1815}";
print "$number is ", unidecode($number), "\n";

经过更多的测试,看起来Text::Unidecode不能正确处理所有的数字字符。我正在编写一个可以工作的module

yqyhoc1h

yqyhoc1h5#

我觉得两者都有各自的位置。然而,99.999%的时候(特别是在我封闭的大美国合作世界里)它们是可以互换的,我每天都使用Perl来操作数据,在我处理的数据集中没有一个数据不符合[0-9]。我很清楚\d[0-9]之间有一个重要的区别,意识到这个区别是很好的。(正如你所说)而且在我的数据操纵小世界里永远不会“错”。

jyztefdp

jyztefdp6#

如果将\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]之间的区别。

ru9i0ody

ru9i0ody7#

如果[0-9]感觉很笨重,也许您可以定义:$d=qr/[0-9]/;并使用该值代替\d

rkkpypqq

rkkpypqq8#

上述使用\d的主要缺点似乎是非ascii数字。
这可以通过/a选项来避免。例如:

m/\d/a

这将数字匹配限制为仅ASCII。

ercv8c1e

ercv8c1e9#

随着数据格式控制的增加,对模式特异性的需求下降了...
例如,如果您要匹配机器生成的数据,并且始终遵循相同的输出格式规则,则不需要如此精确。以IPv4地址为例,如果您要从路由器接口配置行中提取IP地址,则您真正需要的是类似以下内容:

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

如果,另一方面,你试图找到一个IP地址嵌入深的某个地方,比如说,一个电子邮件X头,或者如果你试图验证一个IP地址,那么...这是一个完整的'另一个故事!

相关问题