Интересный вопрос. Это может быть решено с помощью одного оператора preg_replace()
, но длина повторяющейся фразы должна быть ограничена, чтобы избежать чрезмерного возврата. Вот решение с закомментированным регулярным выражением, которое работает для тестовых данных и исправляет удвоенные, утроенные (или повторяющиеся n
раз) фразы, имеющие максимальную длину 50 символов:
Решение к части 1:
$result = preg_replace('/
# Match a doubled "phrase" having length up to 50 chars.
( # $1: Phrase having whitespace boundaries.
(?<=\s|^) # Assert phrase preceded by ws or BOL.
\S # First char of phrase is non-whitespace.
.{0,49}? # Lazily match phrase (50 chars max).
) # End $1: Phrase
(?: # Group for one or more duplicate phrases.
\s+ # Doubled phrase separated by whitespace.
\1 # Match duplicate of phrase.
){1,} # Require one or more duplicate phrases.
/x', '$1', $text);
Обратите внимание, что в этом решении «фраза» может состоять из одного слова, и существуют законные случаи, когда сдвоенные слова являются допустимой грамматикой и не должны быть исправлены. Если приведенное выше решение не является желаемым поведением, регулярное выражение можно легко изменить, чтобы определить «фразу» как два или более «слова».
Редактировать: Модифицировано выше регулярное выражение для обработки любого количества повторений фраз. Также добавлено решение второй части вопроса ниже.
А вот аналогичное решение, где фраза начинается со слова из цифр, а повторяющиеся фразы также должны начинаться со слова из цифр (но первое слово из цифр повторяющихся фраз не обязательно должно совпадать с оригиналом):
Решение к части 2:
$result = preg_replace('/
# Match doubled "phrases" with wildcard digits first word.
( # $1: 1st word of phrase (digits).
\b # Anchor 1st phrase word to word boundary.
\d+ # Phrase 1st word is string of digits.
\s+ # 1st and 2nd words separated by whitespace.
) # End $1: 1st word of phrase (digits).
( # $2: Part of phrase after 1st digits word.
\S # First char of phrase is non-whitespace.
.{0,49}? # Lazily match phrase (50 chars max).
) # End $2: Part of phrase after 1st digits word.
(?: # Group for one or more duplicate phrases.
\s+ # Doubled phrase separated by whitespace.
\d+ # Match duplicate of phrase.
\s+ # Doubled phrase separated by whitespace.
\2 # Match duplicate of phrase.
){1,} # Require one or more duplicate phrases.
/x', '$1$2', $text);