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

thi*_*khy 2 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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

  #after    (?<=\/\/) 
Run Code Online (Sandbox Code Playgroud)

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

ike*_*ami 8

问题在于

^(?<=\/\/)
Run Code Online (Sandbox Code Playgroud)

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

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

(?<=^\/\/)
Run Code Online (Sandbox Code Playgroud)

在做了一些改进之后,代码看起来像:

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

    return $jcl;
}
Run Code Online (Sandbox Code Playgroud)

改进:

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

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

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