我正在为一位同事进行代码审核,他的正则表达式如下:
if ($value =~ /^\d\d\d\d$/) {
#do stuff
}
我告诉他应该把它换成:
if ($value =~ /^\d{4}$/) {
#do stuff
}
他回答说他更喜欢第一个可读性(我发现第二个更具可读性,但这是一个宗教辩论,我将保存另一天)。
我的问题:一个人对另一个人有实际的好处吗?
答案 0 :(得分:15)
没有绝对可读性这样的东西。人们可以单独认识到这一点,这就是人们经常理解他们的代码而其他人无法理解的原因。如果他从不使用量词,他总是会认为量词很难读,因为他从未学会理解量词。
我经常发现,当人们说“我已经知道的东西”或“这就是我第一次写的东西”时,人们会说“更具可读性”。但是,这不一定是这种情况。
像{4}
这样的绝对量词更容易指定并与其他程序员沟通。谁想手动计算\d
的数量?你为其他人写代码来阅读,所以不要让他们的生活更难。
但是,您可能错过了该代码中的错误,因为您专注于量词问题。 $
锚点允许在字符串末尾添加换行符,并且如果 Perl Best Practices 狂热者出现并盲目地将/xsm
添加到所有正则表达式中(这是一次痛苦的经历)已经看过几次了,$
允许更多的无效输出。您可能需要\z
绝对字符串结束锚点。
并非在您的情况下发生这种情况,但代码审查往往会转变为样式或语法评论(因为这些更容易注意)并且实际上忽略了检查正确和预期行为以及正确设计的重点。通常,风格问题不值得考虑考虑所有其他方法来花时间来改进代码。 :)
答案 1 :(得分:12)
他们完全一样,所以就实用性而言,这是一个偏好问题。这种或那种方式之间存在微小的性能差异吗?谁知道,但肯定无足轻重。
当模式长度不固定时,量词更有用(和必需),例如\d{12,16}
,\d{2,}
等。
我更喜欢\d{4}
,这比\d\d\d\d
如果你匹配一个字符类而不是一个简单的数字呢? [aeiouy0-9]{4}
或[aeiouy0-9][aeiouy0-9][aeiouy0-9][aeiouy0-9]
?
答案 2 :(得分:10)
我现在只想回避可读性问题。
首先让我们看看每个版本编译的内容。
perl -Mre=debug -e'/^\d{4}$/'
Compiling REx "^\d{4}$"
synthetic stclass "ANYOF[0-9][{unicode_all}]".
Final program:
1: BOL (2)
2: CURLY {4,4} (5)
4: DIGIT (0)
5: EOL (6)
6: END (0)
anchored ""$ at 4 stclass ANYOF[0-9][{unicode_all}] anchored(BOL) minlen 4
Freeing REx: "^\d{4}$"
perl -Mre=debug -e'/^\d\d\d\d$/'
Compiling REx "^\d\d\d\d$"
Final program:
1: BOL (2)
2: DIGIT (3)
3: DIGIT (4)
4: DIGIT (5)
5: DIGIT (6)
6: EOL (7)
7: END (0)
anchored ""$ at 4 stclass DIGIT anchored(BOL) minlen 4
Freeing REx: "^\d\d\d\d$"
现在我要看看每个版本的表现如何。
#! /usr/bin/env perl
use Benchmark qw':all';
cmpthese( -10, {
'loop' => sub{ 1234 =~ /^\d{4}$/ },
'repeat' => sub{ 1234 =~ /^\d\d\d\d$/ }
});
Rate loop repeat loop 890004/s -- -10% repeat 983825/s 11% --
虽然/^\d\d\d\d$/
确实运行得更快,但速度并不快。这实际上只是让它失去了可读性。
让我们把这个例子推向极致:
/^\d{32}$/;
/^\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d$/;
我认为没有多少人会认为第二个例子更容易阅读。
如果我们把它带到另一个极端,那么第一种风格似乎是多余的。
/^\d{1}$/;
/^\d$/;
因此,在您的偏好从重复\d
转换为使用量词之前,\d
的重复次数是多少。
答案 3 :(得分:5)
任何超过3或4的重复都难以一目了然。我认为这是一个令人信服的理由。最重要的是,使用量词是表达重复信息的“密集”方式。对我而言,这就像复制和粘贴代码“重用”与编写真正可重用代码之间的区别。
答案 4 :(得分:3)
最好认为当他想找到一组10多个字母时,他将不得不使用量词而不是重复,最好习惯于正确的方式,此外,如果他坚持使用重复更大一组字符,在尝试计算它们时会有一些麻烦,如果用量词标记它就不需要。
答案 5 :(得分:2)
{4}
比\d\d\d\d
更容易维护,因为它可以更好地扩展。例如,如果您以后需要将其更改为匹配11位数,则只需将4更改为11,而不必为正则表达式添加14个字符。
答案 6 :(得分:1)
与许多事情一样,这是你想要走多远的问题。
一个真实的例子。
比较
my @lines = $header =~ m/([^\n\r]{13}|[^\n\r]+)/g; #split header into groups of up to 13 characters
到
my @lines = $header =~ m/([^\n\r][^\n\r][^\n\r][^\n\r][^\n\r][^\n\r][^\n\r][^\n\r][^\n\r][^\n\r][^\n\r][^\n\r][^\n\r]|[^\n\r]+)/g; #split into groups of up to 13 characters
你还能找到管道'|'吗?
答案 7 :(得分:0)
根据具体情况,我可能会使用任何一种形式。
让我们忽略自定义字符类的稻草人复杂性,在一行上重复96次,而是专注于编写得很好的代码。
考虑:
$foo =~ m{
(\d\d\d\d)
[ ] (\d\d\d?)
[ ] (\w\w)
}x;
我使用这样的代码来解析天气传感器的数据。我使用这种格式是因为它与制造商的文档非常匹配。这对于“固定宽度”数据格式非常有效,这些格式并不完全符合固定宽度字段的承诺(这在实践中非常常见)。
你可以说我应该把空格放在与前一个字段分开的行或同一行上,而不是与后续字段一致。但这只是格式化,对于perltidy来说确实是一个问题。
在其他情况下,我使用过这样的代码:
$foo =~ m{
( \d{4} )
[ ] ( \d{2,3} )
[ ] ( \w{2} )
}x;
为了保持上述可读性,您必须添加更多空格,并使用格式化更多。
第二种风格可以更好地扩展复杂性 - 添加自定义字符类和宽字段不会破坏可读性。
最重要的是在给定的正则表达式内保持一致。 IOW,永远不要这样做:
$foo =~ m{
( \d\d\d\d )
[ ] ( \d{2,3} )
[ ] ( \w\w )
}x;
最终,代码执行两个功能。最着名的功能是它告诉计算机该做什么。但是最重要但却被忽略的代码功能是告诉维护程序员计算机正在做什么。
答案 8 :(得分:-1)
关于可读性......一些Perl程序员使用非常罕见的功能,希望它们具有可读性,但是,它需要了解这种罕见的功能。
有许多正面新手不明白{4}是什么。
关于好处,第二个可能更好,因为它在regexp引擎中占用较少的数组元素。除非您是真正的程序员,否则您不会将性能优化为纳秒级。