正则表达式:匹配所有行中的子字符串,除非子字符串位于注释部分内

时间:2012-07-29 07:17:08

标签: php regex regex-negation

我走了:

我正在编写一个PHP应用程序,我有一个新的官方域名,现在所有常见问题解答都在这里。我脚本中的一些文件包含旧FAQ域的帮助链接,因此我想使用新域替换它们。但是,我想保留链接到旧域的URL,只要它们位于注释或注释块下面(我仍然使用旧域进行自我引用和其他文档)。

所以,基本上,我想要实现的是一个正则表达式,它具有以下功能:

  1. 匹配所有行中example.com的所有匹配项。
  2. 不匹配整行,只匹配example.com字符串。
    • 如果该行以//开头,/*或“*”与该单行中的任何example.com实例都不匹配(尽管如果评论可能会出现问题)块在它被打开的同一行中关闭。
  3. 我经常写这样的块评论:

    /* text
     * blah 
     * blah
    */
    

    这就是为什么我不想匹配“example.com”,如果它位于///*或“*”之后。

    我认为它会是这样的:

    ^(?:(?!//|/\*|\s\*).?).*example\.com
    

    但是这有一个问题:它匹配整行,而不仅仅是“example.com”(这会导致问题,主要是当两个或多个“example.com”字符串在一行中匹配时)。

    有人可以帮我修复我的正则表达式吗? 请注意:它不一定是PHP正则表达式,因为我总是可以使用像grepWin这样的工具来一次本地编辑所有文件。

    哦,请告诉我是否有办法以某种方式概括阻止评论,例如:找到/*后,example.com*/不匹配找到了。这将非常有用。是否有可能实现一般(非语言相关)正则表达式?

2 个答案:

答案 0 :(得分:2)

只有匹配example.com的正则表达式,如果它不在评论部分内(但不关心行注释,所以你必须单独执行此操作):

$result = preg_replace(
    '%example\.com # Match example.com
    (?!            # only if it\'s not possible to match
     (?:           # the following:
      (?!/\*)      #  (unless an opening comment starts first)
      .            #  any character
     )*            # any number of times
     \*/           # followed by a closing comment.
    )              # End of lookahead
    %sx', 
    'newdomain.com', $subject);

答案 1 :(得分:2)

我会使用某种标记器来区分评论和其他语言标记。

在处理PHP文件时,您应该使用PHP’s own tokenizer function token_get_all

$tokens = token_get_all($source);

然后,您可以枚举令牌并分隔tokens by their type

foreach ($tokens as &$token) {
    if (in_array($token[0], array(T_COMMENT, T_DOC_COMMENT, T_ML_COMMENT))) {
        // comment
    } else {
        // not a comment
        $token[1] = str_replace('example.com', 'example.net', $token[1]);
    }
}

最后,将所有内容与implode重新组合在一起。

对于你手上没有合适的标记器的其他语言,你可以编写自己的小标记器:

preg_match_all('~/\*.*?\*/|//(?s).*|(example\.com)|.~', $code, $tokens, PREG_SET_ORDER);
foreach ($tokens as &$token) {
    if (strlen($token[1])) {
        $token = str_replace('example.com', 'example.net', $token[1]);
    } else {
        $token = $token[0];
    }
}
$code = implode('', $tokens);

请注意,这不会考虑任何其他令牌,如字符串。因此,如果example.com出现在字符串中,而且还出现在“评论”中,则不会与'foo /* not a comment example.com */ bar' 匹配:

{{1}}