正则表达式匹配中间但最后的单词? (php崩溃:(

时间:2015-07-08 21:47:11

标签: php regex preg-replace

我想找到标签和返回之间没有行缩进的行。 例如:

myLabel:
bla
if(no)
  return
else
  foo
return

如果我最后一次使用其他词语。例如发送它有效。

$r1 = '^(\w[\w\d_]*:\s*\n((?!\nreturn).)*)(\n[^\s][^n]*\n)((((?!\nreturn).)*)\nsend)'; ; working regex

但$ r2不起作用。 Perl崩溃。

$r2 = '^(\w[\w\d_]*:\s*\n((?!\nreturn).)*)(\n[^\s][^n]*\n)((((?!\nreturn).)*)\nreturn)'; ; dont working regex

以下是php中用于测试的示例

$str = '^(\w[\w\d_]*:\s*\n((?!\nreturn).)*)(\n[^\s][^n]*\n)((((?!\nreturn).)*)\nreturn)';
$actual = preg_replace('/^'.$str.'/smi', "$1" . $indentStr . "$2$3", $actual);

如果这不起作用,那么我将使用循环抛出所有源代码行。 我将使用它来使用此工具来完善Autohotkey源代码:https://github.com/sl5net/SL5_AHK_Refactor_engine

2 个答案:

答案 0 :(得分:2)

你的模式非常复杂,使用着名的#34;技巧:((?!\nreturn).)*这个很慢,如果子模式失败后也不会阻止很多回溯。

您可以用更简单的方式编写模式:

$pattern = '~^\w+:\R(?:\N*\R)*?return$~m';

demo

细节:

~            # pattern delimiter
^            # anchor for the start of the line (m option)
\w+:         # the label name
\R           # alias for any kind of newline sequences
(?:\N*\R)*?  # lines until (non-greedy number of line)
return       # "return"
$            # end of the line (remove it if uneeded)
~m           # pattern delimiter, multiline option

\N匹配除换行符之外的任何字符(无论是否为单行)。在这种情况下,您可以用点替换它,但它不太明确。

\R是几个新行序列\r\n\n或更具异国情调的别名。如果您已经知道字符串中使用了哪种换行符序列,请将其替换为此序列。

see this other version

简而言之,该模式旨在测试一行是否以" return"但只在行的开头(不是在字符串的所有位置)。

答案 1 :(得分:0)

我找到了一个实现。它完美无缺。它缩进标签正文并且不会打扰周围的内容。以下是实施:https://github.com/sl5net/SL5_AHK_Refactor_engine/blob/master/phpdesktop-msie-1.14-php-5.4.33/www/SL5_preg_contentFinder/examples/AutoHotKey/Reformatting_Autohotkey_Source.php#L192

$pattern = '/^(\w+:)(\h*\n)(?:.*\n)*?(return)/m';
$label = '^\w[\w\d_]*:';
$pattern = '/' . "($label)(\h*\R)((?:.*\n)*?)(return\b)" . '/im';
preg_match_all($pattern, $actual, $matches,PREG_OFFSET_CAPTURE);
$labelsAr = $matches[1];
$contentAr = $matches[3];
$returnAr =  $matches[4];
for($k = count($labelsAr) ; $k-- ; $k >=0 ) {
    $new = $labelsAr[$k][0]
      . "\n" . $indentStr
      . rtrim( preg_replace('/\n/ism', "\n"
        . $indentStr, $contentAr[$k][0]) )
      . "\n" . ltrim($returnAr[$k][0]) ;
    $actual = substr($actual,0,$labelsAr[$k][1])
      . $new
      . substr($actual,$returnAr[$k][1] + strlen($returnAr[$k][0]) ) ;
}