将多个空格,制表符和换行符替换为除注释文本之外的一个空格

时间:2013-06-23 15:25:20

标签: php preg-replace

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

<br/>    <br>

<!--
this   is a comment

-->
<br/>   <br/>

应该变成

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

--><br/><br/>

有什么想法吗?

4 个答案:

答案 0 :(得分: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将不存在,并且将不存在替换(这将导致删除空格......)。如果有评论,那么评论将被评论(lol)替换。

Online demo


解决方案

我想出了一个新策略,这段代码需要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)

Online demo

  

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

答案 1 :(得分:1)

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

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

Step2:根据需要preg_replace()个空格。

第3步:str_replace()回复他们最初使用键控数组的位置。

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

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

答案 2 :(得分:1)

您可以使用:

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

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

答案 3 :(得分:1)

我会做以下事情:

  1. 将输入拆分为评论和非评论部分
  2. 替换非评论部分
  3. 把所有东西放回原处
  4. 示例:

    $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);