正则表达式匹配类似Razor的表达式

时间:2011-10-14 11:35:59

标签: regex regex-negation

我一直在试图弄清楚如何匹配类似Razor的嵌入式表达式。这不是真正的Razor语法,只是类似的东西。

示例:

给出以下字符串:

  

这个@ ShouldMatch1和这个@ ShouldMatch2就是这个   @((ShouldNotMatch1)和这个@ ShouldMatch3)和这个@(ShouldNotMatch2   这个@ 1ShouldNotMatch3和这个@((ShouldNotMatch4和这个   @(ShouldMatch4))

  • 匹配和捕获:
    • ShouldMatch1
    • ShouldMatch2
    • ShouldMatch3
    • ShouldMatch4

基本上,这是requiremetns:

  • 如果它以@开头然后是[a-zA-Z] + [0-9] *那么我想匹配它。
  • 如果它以@开头(那么我只想匹配,如果它后跟[a-zA-Z] + [0-9] *然后a)。

这是我作为一个开始,它在大多数情况下工作,但它匹配 ShouldNotMatch2

\@[(]?([a-zA-Z]+[0-9]*[)]*)

3 个答案:

答案 0 :(得分:3)

如果你的正则表达式引擎支持条件:

@(\()?([A-Za-z]+[0-9]*)(?(1)\))

<强>解释

@           # Match @
(\()?       # Optionally match `(` and capture in group 1
(           # Match and capture in group 2
 [A-Za-z]+  # 1+ ASCII letters
 [0-9]*     # 0+ ASCII digits
)           # End of capturing group
(?(1)       # If group 1 participated in the match
 \)         # match a closing parenthesis
)           # End of conditional

答案 1 :(得分:2)

此代码:

#!/usr/bin/env perl

$_ = <<'LA VISTA, BABY';  # the Terminator, of course :)
    This @ShouldMatch1 and this @ShouldMatch2 and this @((ShouldNotMatch1)andthis@ShouldMatch3) and this @(ShouldNotMatch2 an
d this @1ShouldNotMatch3 and this @((ShouldNotMatch4 and this @(ShouldMatch4))'
LA VISTA, BABY

print $+{id}, "\n" while m{
    @ (?: \(  (?<id> \pL+ \d* )  \)
        |     (?<id> \pL+ \d* )
      )
}gx;

运行时会打印出您想要的输出:

ShouldMatch1
ShouldMatch2
ShouldMatch3
ShouldMatch4

修改

以下是从简单到发烧友的前一个解决方案的五个阶段的详细说明。但是,仍然不清楚标识符的真正规则是什么,或者应该是什么。

  1. 原始问题:\pL+\d*这说它以字母开头,然后可能以数字结尾,但不一定。
  2. \pL+\d+强制数字。
  3. \pL[\pL\d]*必须以字母开头,但允许字母和数字混合。
  4. \pL\w*为首字母后面的内容添加了下划线。从技术上讲,根据UTS#18\w应该是所有字母,所有标记,字母数字(如罗马数字),所有十进制数字,加上所有连接符标点符号。
  5. \w+适用于所有字母,数字或下划线,无限制。这就是字符符合标准的字样。
  6. (?=\pL)\w+(?<=\d)添加一个约束,它必须以字母开头并以数字结尾,否则可以是单词字符的组合。
  7. 无论实际需要哪一个 - 目前还不清楚 - 应该很容易更新代码以使用适当的变体,特别是在最后两个版本中,其中定义的内容只是作为这些有趣的标识符出现在代码中的一个地方。这使得在一个地方轻松更改它并摆脱更新 - 不一致的错误。程序员应该总是努力分解重复的代码,无论他们的编程语言,甚至是正则表达式都是如此,因为抽象是良好设计的基础。

    然后是5向版本:

    #!/usr/bin/env perl
    
    $_ = <<'LA VISTA, BABY';  # the Terminator, of course :)
        This @ShouldMatch1 and this @ShouldMatch2 and this @((ShouldNotMatch1)andthis@ShouldMatch3) and this @(ShouldNotMatch2 and this @1ShouldNotMatch3 and this @((ShouldNotMatch4 and this @(ShouldMatch4))'
    LA VISTA, BABY
    
    $mask  = "Version %d: %s\n";
    $verno = 0;
    
    ##########################################################
    # Simplest version: nothing fancy
    ++$verno;
    printf($mask, $verno, $+) while /\@(?:(\pL+\d*)|\((\pL+\d*)\))/g;
    print "\n";
    
    ##########################################################
    # More readable version: add /x for spacing out regex contents
    ++$verno;
    printf($mask, $verno, $+) while / \@ (?: (\pL+\d*) | \( (\pL+\d*) \) ) /xg;
    print "\n";
    
    ##########################################################
    # Use vertical alignment for greatly improved legibility,
    # plus named captures for convenience and self-documentation
    ++$verno;
    printf($mask, $verno, $+{id}) while m{
        @ (?: \(  (?<id> \pL+ \d* )  \)
            |     (?<id> \pL+ \d* )
          )
    }xg;
    print "\n";
    
    ##########################################################
    # Define the "id" pattern separately from executing it
    # to avoid code duplication. Improves maintainability.
    # Likely requires Perl 5.10 or better, or PCRE, or PHP.
    ++$verno;
    printf($mask, $verno, $+)     while m{
        (?(DEFINE)  (?<id> \pL+ \d* )   )
    
        @ (?: \( ((?&id)) \)
            |    ((?&id))
          )
    }xg;
    print "\n";
    
    ##########################################################
    # this time we use a named capture that is different from
    # the named group used for the definttion.
    ++$verno;
    printf($mask, $verno, $+{id}) while m{
        (?(DEFINE)  (?<word> \pL+ \d* )   )
    
        @ (?: \( (?<id> (?&word) ) \)
            |    (?<id> (?&word) )
          )
    }xg;
    

    在Perl v5.10或更高版本上运行时,它会产生:

    Version 1: ShouldMatch1
    Version 1: ShouldMatch2
    Version 1: ShouldMatch3
    Version 1: ShouldMatch4
    
    Version 2: ShouldMatch1
    Version 2: ShouldMatch2
    Version 2: ShouldMatch3
    Version 2: ShouldMatch4
    
    Version 3: ShouldMatch1
    Version 3: ShouldMatch2
    Version 3: ShouldMatch3
    Version 3: ShouldMatch4
    
    Version 4: ShouldMatch1
    Version 4: ShouldMatch2
    Version 4: ShouldMatch3
    Version 4: ShouldMatch4
    
    Version 5: ShouldMatch1
    Version 5: ShouldMatch2
    Version 5: ShouldMatch3
    Version 5: ShouldMatch4
    

    更新id的定义以匹配实际需要的内容应该很容易。

    请注意,某些正则表达式引擎会使指定属性变得非常麻烦。例如,他们可能需要\p{L}而不是正常\pL。这是一个霍夫曼编码失败的错误设计,因为你总是希望最常用的形式是最短的形式。由于\pL\pN只比\w\d长一个字符,所以人们更倾向于使用改进的版本,但\p{L}之类的内容而且\p{N}现在比\w\d长三个字符,加上不必要的视觉混乱才能启动。您不应该为了获得“正常”ᴀᴋᴀ最常见的情况而支付三倍。 :(

    如果 要将丑陋的括号放入其中,那么您也可以将该内容全部写为\p{Letter}\p{Number}。毕竟,正如他们所说的那样,“为了一分钱,为了一磅,”

答案 2 :(得分:1)

它可以是@(foo1)@foo1,因此请使用替换:

@([a-zA-Z]+[0-9]|\([a-zA-Z]+[0-9]\))

并在第二步中删除括号(简单的字符串替换)。