Удалить повторяющиеся последовательные слова из строки

Я хочу удалить повторяющиеся слова из строки (только подряд).

$str = 'abc,def,fgh,fgh,xna,fgh,xyz,xyz,xyz,tr,tr,xna';

Моя желаемая выходная строка:

abc,def,fgh,xna,fgh,xyz,tr,xna

Я могу получить результат, который хочу в php, используя это:

$ip = explode(',', $str);
$op = [];$last = null;
for($i=0;$i<count($ip);$i++){
    if ($last == $ip[$i]) {
        continue;
    }
    $op[]=$last=$ip[$i];
}
$ip = implode(',', $op);

Но искал подход регулярных выражений. До сих пор я приблизился к этим двум регулярным выражениям:

$after = preg_replace('/(?:^|,)([^,]+)(?=.*,\1(?:,|$))/m', '', $str);
output : abc,def,fgh,xyz,tr,xna

$after = preg_replace('/([^,]+)(,[ ]*\1)+/m', '', $str);
output : abc,degh,fgh,xna,fgh,,,xna
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
6
0
271
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

$after = preg_replace('/(?<=^|,)([^,]+)(,\s*\1)+/', '$1', $str);

P.S. Вы можете избавиться от \s* из приведенного выше регулярного выражения, если после , нет ожидающего пробела. Я только что посмотрел на ваш [ ]* и подумал, что у вас могут быть пробелы.

(извините за получение и публикацию ошибочного вывода из вашего решения, поскольку ранее мне не удалось правильно скопировать это регулярное выражение.)

Progrock 22.07.2019 07:07
Ответ принят как подходящий

Вы должны использовать

preg_replace('~(?<![^,])([^,]+)(?:,\1)+(?![^,])~', '$1', $str)

См. демонстрация регулярных выражений

Если необходимо поддерживать любые 0 или более пробельных символов между запятыми и повторяющимися значениями, шаблон добавить \s* (0 или более пробелов) перед \1.

Подробности

  • (?<![^,]) - начало строки или любой символ кроме запятой
  • ([^,]+) - Группа 1: любой один или несколько символов, кроме запятой
  • (?:,\1)+ - одна или несколько последовательностей запятой и значение в группе 1
  • (?![^,]) - конец строки или символ, отличный от запятой.

@Progrock Это выражение не генерируется автоматически, дело в том, что 1) вам нужна начальная граница, 2) повторяющийся шаблон внутри группы захвата, 3) группа с разделителем и обратная ссылка на группу 1, которая должна повторяться 1 или более раз, а затем 4) замыкающая граница. Без границ часть значения, зафиксированная в группе 1 могут быть сопоставлены ошибочно.

Wiktor Stribiżew 21.07.2019 23:40

Итерируя с помощью strtok, склеивайте только те части, которые не похожи на последние:

<?php

$str = 'abc,def,fgh,fgh,xna,fgh,xyz,xyz,xyz,tr,tr,xna';

$out = $last = strtok($str, ',');
while($current = strtok(','))
    if ($current !== $last)
        $out .= ',' . ($last = $current);

echo $out;

Выход:

abc,def,fgh,xna,fgh,xyz,tr,xna

Я бы решил это так:

$after = preg_replace('/(?<=,|^)([^,]+)\K(,\1)+(?=,|$)/', '', $str);

Это выведет abc,def,fgh,xna,fgh,xyz,tr,xna.

Что оно делает:

  • (?<=,|^) проверяет, является ли запятая или строка начинается прямо перед
  • ([^,]+) соответствует чему угодно, кроме запятых (шаблон поиска)
  • \K сбрасывает внутренний курсор и «забывает» то, что было раньше (например, оно больше не считается совпавшим)
  • (,\1)+ соответствует нескольким появлениям первого определенного шаблона поиска
  • (?=,|$) проверяет, будет ли следующий символ снова запятой или строка заканчивается

Итак, идея состоит в том, чтобы выбрать повторы любого шаблона (только повторы) и ничем их не заменить.

Обновлять:

Исправил шаблон, добавив (?=,|$). В противном случае этот тестовый ввод не удастся и полностью убейте часть xna.

$str = 'bc,abc,abc,abc,def,fgh,fgh,xna,fgh,xyz,xyz,xyz,tr,tr,xna,xna,xnabc';

Проверьте это здесь: https://regex101.com/r/Yv1htV/3

С array_reduce:

$arr = explode(',', $str);
$prev = array_shift($arr);
$result = array_reduce($arr, function($c, $i) use (&$prev) {
    if ($prev==$i) return $c;
    $prev=$i;
    return "$c,$i";
}, $prev);

Другие вопросы по теме