为什么零宽度匹配正则表达式工作?

时间:2012-09-06 15:29:24

标签: regex perl mainframe jcl

我写了一个Perl函数来替换JCL脚本中的作业名称。这里使用了零宽度匹配。

sub modify_jcl_jobname ()
{
    my ($jcl, $old, $new) = @_;

    $jcl =~ s/
         # The name must begin in column 3.
         ^(?<=\/\/)     

         # The first charater must be alphabetic or national.
        ($old)

         # The name must be followed by at leat on blank.
         # Append JCL keyword JOB 
        (?=\s+JOB)
       /$new/xmig; # Multi-lines, ignore case.

    return $jcl;
}

但是直到我做了一个简单的修改才删除了前导符号“^”,这个功能才起作用。

  #before  ^(?<=\/\/) 

  #after    (?<=\/\/) 

所以我想说明问题的原因。任何回复将不胜感激。感谢。

2 个答案:

答案 0 :(得分:8)

问题在于

^(?<=\/\/)

只有^匹配的地点前面有两个字符//时,该模式才会匹配。这是永远不会发生的,因为/^/m匹配字符串的开头和换行符之后。

但是你不想在行的开头开始匹配。你想开始匹配2个字符。你想要的实际上是:

(?<=^\/\/)

在做了一些改进后,代码如下:

sub modify_jcl_jobname {
    my ($jcl, $old, $new) = @_;
    $jcl =~ s{
         (?<= ^// )
         \Q$old\E
         (?= \s+ JOB )
    }{$new}xmig;

    return $jcl;
}

改进:

  • 删除了错误的原型(())。它强制调用者告诉Perl忽略原型(使用&)。
  • 添加代码(\Q...\E)以将$old的内容转换为正则表达式模式,然后再使用它。
  • 删除了不必要的捕获((...))。
  • 切换替换的分隔符(从s///s{}{})以减少转义。
  • 删除了高度冗余的评论。 (好的评论解释为什么正在做某事而不是正在做什么。)

优化器可能会更好地处理此版本:

$jcl =~ s{
     ^// \K
     \Q$old\E
     (?= \s+ JOB )
}{$new}xmig;

答案 1 :(得分:3)

^符号与该行的开头匹配。然后你想要一些前面有两个斜杠的东西 - 如果下一个字符是该行的第一个字符,这些斜杠应该在哪里?

s{^//
  ($old)
  ...
 }{//$new}xmig

应该有效:你不需要看后面。

更新:感谢ikegami,我现在明白你使用它的原因了。您希望将//保留在字符串中:嗯,您可以在替换中重复这些内容,或将^字符移动到后面。