是否有令人信服的理由在Perl正则表达式中使用量词而不是仅仅重复该字符?

时间:2010-03-30 18:23:40

标签: regex perl

我正在为一位同事进行代码审核,他的正则表达式如下:

if ($value =~ /^\d\d\d\d$/) {
    #do stuff
}

我告诉他应该把它换成:

if ($value =~ /^\d{4}$/) {
    #do stuff
}

他回答说他更喜欢第一个可读性(我发现第二个更具可读性,但这是一个宗教辩论,我将保存另一天)。

我的问题:一个人对另一个人有实际的好处吗?

9 个答案:

答案 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引擎中占用较少的数组元素。除非您是真正的程序员,否则您不会将性能优化为纳秒级。