使用一个正则表达式在多行注释中查找单词

Find a word in multiline comment with one regex

我需要一个正则表达式来匹配位于多行注释 /* ... */ 中的特定捕获组。

特别是我需要在多行注释中找到 PHP 变量定义

例如:

/* other code $var = value1 */
$var = value2 ;

/* 
other code
$var = value3 ;
other code
*/

必须只匹配注释中出现的两次“$var =”,而不匹配注释外的一次。

对于上面的例子,我写了一个正则表达式,它使用不受限制的回顾,就像这样

(?<=[/][\*][^/]+)($var) | (?<=[/][\*][^\*]+)($var)

但是这个正则表达式会失败,以防它在注释开始标记“/*”和 $var 之间找到字符 * 和 /,即使它们彼此分开,这不是所需的行为:

例如在以下情况下失败:

$var = .... ;

/* 
other * code /
$var = .... ;
other code
*/

因为即使它不是评论结束标记,它也会找到“*”和“/”。

关键是我不能对两个字符组合的token取反,只能一个一个取反:[^*]或[^/]。

...此外,我不能使用令牌 [\s\S] 代替 [^/] 和 [^*],因为它会 select $var 超出前一个块之前的注释的评论。

有什么想法吗?使用普通正则表达式甚至有可能实现这一目标吗?或者我需要一些不同的东西吗?

类似这样的方法可能有效:

/\/\*.*?$var\s*\=\s(.*?)(?=\s*;)/s

用法:

$str = '$var = .... ;
/*
other code
$var = ..... ;
other code
*/';
preg_match('/\/\*.*?$var\s*\=\s(.*?)(?=\s*;)/s', $str, $matches);

var_dump($matches);

将输出:

array(2) {
  [0]=>
  string(26) "/*
other code
$var = ....."
  [1]=>
  string(5) "....."
}

并且您的字符串存储在 $matches[1]

Try it online

怎么样:

$str = '
/* other code */
$var = "var1";

/* 
other code
$var = "var2";
other code
*/
/* other code */
$var = "var3";

/* 
other code / <-- a slash here
$var = "var4";
other code
*/';

preg_match_all('~/\*(?:(?!\*/).)+?($var = .+?;).*?\*/~s', $str, $m);
print_r($m[1]);

输出:

Array
(
    [0] => $var = "var2";
    [1] => $var = "var4";
)

这只匹配 $var,并且只在多行注释中:

(?s)$var(?=(?:(?!/\*|\*/).)*\*/)

DEMO

(?:(?!/\*|\*/).)* 是一种强制性前瞻(也称为 Tempered Greedy Token——好名字,但音节太多),这是排除序列而不是单个字符的方式。这个匹配零个或多个任何字符(包括换行符,因为 (?s)),只要它不是 /**/ 的第一个字符。

如果在没有首先遇到 /* 的情况下找到 */,则封闭前瞻会成功。这意味着当前位置必须在评论内(不需要匹配开头 /*)。由于前瞻不消耗任何字符,因此如果需要,您可以在每个评论中匹配多个项目。

可以骗过这个正则表达式的是 */ 这不是真正的注释。所以这些:

$var = "*/";

$var = ...;
// */

...会匹配,即使他们不在评论中。

使用 \G to glue 的想法与 /*

匹配
(?:/\*|\G(?!^))(?:(?!\*/)[^$])*\K$var\s*=\s*(?:(?!\*/)[^$;])*

如果您不经常使用正则表达式,可能很难理解。 See regex101 for demo.

\G可以看作是"glue",它在上一场比赛结束时继续。但是 \G 也匹配字符串的开头。这就是为什么使用负前瞻\G(?!^)只需要继续。

  • /\*|\G(?!^)这部分是在/*找到一个匹配的开始或者继续匹配。

  • (?:(?!\*/)[^$])* 匹配任意数量的不是 $ 的字符(取反 class),同时不结束对 stuff [=] 的注释 (?!\*/) 66=]$var

  • \K$var \K resets$var 发生之前报告的匹配开始。 \K 可用作 pcre 中不可用的可变宽度 lookebhind 的替代方法。

  • \s*=\s*(?:(?!\*/)[^$;])*来匹配变量的值。这远非完美。如果 quoted values 或不方便您输入,则需要修改。在 = 之后它匹配 [^$;] 个字符,这些字符不是美元或分号 (?!\*/) 只要前面没有 */

这个正则表达式不检查是否真的有注释结束 */ 它只是将匹配项绑定到 /*
另一个想法是使用 this trick with verbs (*SKIP)(*FAIL) like in this demo.

尝试 php,但 java 有效

(?s)(?i)(^|\s+?)(/*)((.)(?!*/))?(这)(.?)(*/)

in this example finding word is "this"