复杂的PHP空白删除

时间:2018-05-15 10:55:39

标签: php string text-parsing

有很多关于删除空格的问题,通常用preg_replace('/[\s]{2,}/, '', $string)或类似的答案来回答,这些答案需要多个空白字符,然后将其删除或替换为其中一个字符。

当允许某些空白复制时(例如,具有两个换行符和一个换行符允许且相关的文本块),这会变得更复杂,而且结合空白字符(\n\r)。

这是一些示例文本,虽然凌乱,但却涵盖了我认为最终可能以合理的方式呈现的内容(例如,以前使用HTML格式化的用户输入,现在已被删除)

$text = "\nDear Miss           Test McTestFace,\r\n  \n We  have received your customer support request about:\n \tA bug on our website\n \t \n \n \n We will be in touch by : \n\r\tNext Wednesday. \n   \r\n   \n     Thank you for your custom; \n   \r  \t     \n       If you have further questions please feel free to email us. \n     \n\r\n     \n     Sincerely \n \n    Customer service team \n \n";

如果我们的目标是采用以下格式:

  

亲爱的Miss McTestFace小姐,

     

我们已收到您的客户支持请求:我们的错误   网站

     

我们将在下周三与您联系。

     

感谢您的定制;

     

如果您还有其他问题,请随时给我们发电子邮件。

     

此致

     

客户服务团队

我们如何实现这一目标 - 简单的正则表达式,更复杂的迭代,或者已经存在可以执行此操作的库?

还有哪些方法可以使测试用例更复杂,从而提供更强大的整体算法?

2 个答案:

答案 0 :(得分:1)

就我自己而言,我选择尝试基于这样一种思想的迭代算法:如果我们知道当前的上下文(我们是在段落中,还是在一系列换行符/空格中?),我们就可以做出更好的决策。 / p>

在这种情况下,我选择忽略标签问题,并有兴趣了解它们如何适应假设 - 在这种情况下,我只是将它们剥离出来。

function strip_whitespace($string){
    $string = trim($string);
    $string = str_replace(["\r\n", "\n\r"], "\n", $string);

    // These three could be done as one, but splitting out
    // is easier to read and modify/play with
    $string = str_replace("\r", "\n", $string);
    $string = str_replace(" \n", "\n", $string);
    $string = str_replace("\t", '', $string);

    $string_arr = str_split($string);
    $new_chars = [];

    $prev_char_return = 0;
    $prev_char_space = $had_space_recently = false;
    foreach ($string_arr as $char){
        switch ($char){
            case ' ':
                if ($prev_char_return || $prev_char_space){
                    continue 2;
                }
                $prev_char_space = true;
                $prev_char_return = 0;
            break;
            case "\n":
            case "\r":
                if ($prev_char_return>1 || $had_space_recently){
                    continue 2;
                }
                if ($prev_char_space){
                    $had_space_recently = true;
                }
                $prev_char_return += 1;
                $prev_char_space = false;
            break;
            default:
                $prev_char_space = $had_space_recently = false;
                $prev_char_return = 0;
        }
        $new_chars[] = $char;
    }

    $return = implode('', $new_chars);
    // Shouldn't be necessary as we trimmed to start, but may as well
    $return = trim($return);

    return $return;
}

我仍然有兴趣看到其他想法,特别是对于这种类型的函数的明显解释与此函数产生的不同的任何文本。

答案 1 :(得分:0)

基于示例(而不是查看您的代码),看起来规则是:

  • 包含至少2个LF字符的空格跨度 是段落分隔符(因此将其转换为空行);
  • 任何其他空格跨度都是单词分隔符 (所以将它转换为单个空格)。

如果是这样,那么一种方法是:

  1. 找到段落分隔符并将它们转换为文本中不会出现的字符串(不涉及空格)。
  2. 将剩余的空格转换为单空格。
  3. 将paragraph-separator-indicators转换为\ n \ n。
  4. E.g:

    $text = preg_replace(
        array('/\s*\n\s*\n\s*/', '/\s+/', '/<PARAGRAPH-SEP>/'),
        array('<PARAGRAPH-SEP>', ' ',     "\n\n"),
        trim($text)
    );
    

    如果规则更复杂,那么使用preg_replace_callback可能更好,例如:

    $text = preg_replace_callback('/\s+/', 'handle_whitespace', trim($text));
    
    function handle_whitespace($matches)
    {
        $whitespace = $matches[0];
    
        if (substr_count($whitespace, "\n") >= 2)
        {
            // paragraph-separator: replace with blank line
            return "\n\n";
        }
        else
        {
            // everything else: replace with single space character
            return " ";
        }
    }