Сложное удаление пробелов в PHP - PullRequest
0 голосов
/ 15 мая 2018

В SO есть несколько вопросов об удалении пробелов, на которые обычно отвечают 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";

Если нашей целью было иметь его в формате:

Уважаемая мисс Тест McTestFace,

Мы получили ваш запрос в службу поддержки о: Ошибка на нашем сайт

Мы свяжемся с вами: в следующую среду.

Спасибо за ваш заказ;

Если у вас есть дополнительные вопросы, пожалуйста, напишите нам.

С уважением

Служба поддержки клиентов

Как бы мы этого достигли - простое регулярное выражение, более сложная итерация или уже есть библиотеки, которые могут это сделать?

Также есть ли способы сделать тестовый пример более сложным и, таким образом, дать более надежный общий алгоритм?

Ответы [ 2 ]

0 голосов
/ 16 мая 2018

Исходя из примера (и не смотря на ваш код), похоже, что правило таково:

  • диапазон пробелов, содержащий как минимум 2 LF-символа, является разделителем абзацев (поэтому преобразуйтеэто пустая строка);
  • любой другой интервал пробелов является разделителем слов (поэтому преобразуйте его в один пробел).

Если это так, то один подход будетto:

  1. Найдите разделители абзацев и преобразуйте их в некоторую строку (без пробелов), которая иначе не встречается в тексте.
  2. Преобразование оставшихся пробелов в пробел.
  3. Преобразовать индикаторы-разделители абзацев в \ n \ n.

Например:

$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 " ";
    }
}
0 голосов
/ 15 мая 2018

Что касается меня, я решил попробовать итеративный алгоритм, основанный на идее, что, если мы знаем текущий контекст (мы в абзаце или в серии разрывов / пробелов?), Мы можем принимать лучшие решения.

Я решил проигнорировать проблему вкладок в этом случае, и мне было бы интересно посмотреть, как они вписываются в предположения - в этом случае я просто исключил их.

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;
}

IМне все еще интересно увидеть другие идеи, и особенно любой текст, чья очевидная интерпретация для функции этого типа будет отличаться от того, что производит эта функция.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...