На 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,
Мы получили ваш запрос поддержки клиентов о: Ошибка на нашем
Веб-сайтМы будем на связи: в следующую среду.
Спасибо за ваш заказ;
Если у вас есть дополнительные вопросы, пожалуйста, напишите нам.
Искренне
Команда обслуживания клиентов
Как бы мы достигли этого — простое регулярное выражение, более сложная итерация или уже есть библиотеки, которые могут это сделать?
Также есть ли способы сделать тестовый пример более сложным и, таким образом, дать более надежный общий алгоритм?
Что касается меня, я решил попробовать итеративный алгоритм, основанный на идее, что, если мы знаем текущий контекст (мы в абзаце или в серии разрывов / пробелов?), Мы можем принимать лучшие решения.
Я решил проигнорировать проблему вкладок в этом случае, и мне было бы интересно посмотреть, как они вписываются в предположения — в этом случае я просто удалил их.
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;
}
Мне все еще интересно увидеть другие идеи, и особенно любой текст, чья очевидная интерпретация для функции этого типа будет отличаться от того, что производит эта функция.
Исходя из примера (и не смотря на ваш код), похоже, что правило таково:
Если это так, то одним из подходов будет:
Например.:
$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 " ";
}
}