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

На 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,

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

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

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

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

Искренне

Команда обслуживания клиентов

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

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

0

Решение

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

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

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

Другие решения

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

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

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

  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

По вопросам рекламы [email protected]