2

我需要将多个空格、制表符和换行符替换为一个空格,但我的 html 中的注释文本除外。例如下面的代码:

<br/>    <br>

<!--
this   is a comment

-->
<br/>   <br/>

应该变成

<br/><br><!--
this   is a comment

--><br/><br/>

有任何想法吗?

4

4 回答 4

5

的解决方案

经过一番思考,我想出了以下纯正则表达式的解决方案。请注意,此解决方案将删除换行符/制表符/多空格而不是替换它们:

$new_string = preg_replace('#(?(?!<!--.*?-->)(?: {2,}|[\r\n\t]+)|(<!--.*?-->))#s', '$1', $string);
echo $new_string;

解释

(?                              # If
    (?!<!--.*?-->)              # There is no comment
        (?: {2,}|[\r\n\t]+)     # Then match 2 spaces or more, or newlines or tabs
    |                           # Else
        (<!--.*?-->)            # Match and group it (group #1)
)                               # End if

所以基本上当没有评论时,它会尝试匹配空格/制表符/换行符。如果它确实找到它,那么组 1 将不存在并且不会有替换(这将导致删除空格......)。如果有评论,则评论将替换为评论(大声笑)。

在线演示


的解决方案

我想出了一个新策略,这段代码需要 PHP 5.3+:

$new_string = preg_replace_callback('#(?(?!<!--).*?(?=<!--|$)|(<!--.*?-->))#s', function($m){
    if(!isset($m[1])){ // If group 1 does not exist (the comment)
        return preg_replace('#\s+#s', ' ', $m[0]); // Then replace with 1 space
    }
    return $m[0]; // Else return the matched string
}, $string);

echo $new_string; // Output

解释正则表达式:

(?                      # If
    (?!<!--)            # Lookahead if there is no <!--
        .*?             # Then match anything (ungreedy) until ...
        (?=<!--|$)      # Lookahead, check for <!-- or end of line
    |                   # Or
        (<!--.*?-->)    # Match and group a comment, this will make for us a group #1
)
# The s modifier is to match newlines with . (dot)

在线演示

注意:您所问的内容和您提供的预期输出内容有点矛盾。无论如何,如果您想删除而不是替换为 1 个空格,那么只需将代码从'#\s+#s', ' ', $m[0]编辑为'#\s+#s', '', $m[ 0]

于 2013-06-23T16:39:01.603 回答
1

在多次运行中执行此操作要简单得多(例如在 php markdown 中所做的)。

第 1步:preg_replace_callback()所有具有独特内容的评论,同时将其原始值保留在键控数组中——例如:array('comment_placeholder:' . md5('comment') => 'comment', ...)

Step2:preg_replace()根据需要留白。

Step3:str_replace()评论他们最初使用键控数组的位置。

您倾向于的方法(拆分字符串并仅处理非注释部分)也可以正常工作。

几乎可以肯定有一种方法可以使用纯正则表达式,使用丑陋的后视,但不推荐:正则表达式可能会产生与回溯相关的错误,并且评论替换步骤允许您在需要时进一步处理事情而不必担心评论他们自己。

于 2013-06-23T15:53:42.260 回答
1

我会做以下事情:

  1. 将输入分成评论和非评论部分
  2. 对非注释部分进行更换
  3. 把所有东西重新组合在一起

例子:

$parts = preg_split('/(<!--(?:(?!-->).)*-->)/s', $input, -1, PREG_SPLIT_DELIM_CAPTURE);
foreach ($parts as $i => &$part) {
    if ($i % 2 === 0) {
        // non-comment part
        $part = preg_replace('/\s+/', ' ', $part);
    } else {
        // comment part
    }
}
$output = implode('', $parts);
于 2013-06-23T17:01:50.400 回答
1

你可以使用这个:

$pattern = '~\s*+(<br[^>]*>|<!--(?>[^-]++|-(?!->))*-->)\s*+~';
$replacement = '$1';
$result = preg_replace($pattern, $replacement, $subject);

此模式捕获br标记和注释,并匹配周围的空格。然后它用捕获组替换匹配项。

于 2013-06-23T16:39:26.467 回答