Я хочу удалить повторяющиеся слова из строки (только подряд).
$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






$after = preg_replace('/(?<=^|,)([^,]+)(,\s*\1)+/', '$1', $str);
P.S. Вы можете избавиться от \s* из приведенного выше регулярного выражения, если после , нет ожидающего пробела. Я только что посмотрел на ваш [ ]* и подумал, что у вас могут быть пробелы.
Вы должны использовать
preg_replace('~(?<![^,])([^,]+)(?:,\1)+(?![^,])~', '$1', $str)
См. демонстрация регулярных выражений
Если необходимо поддерживать любые 0 или более пробельных символов между запятыми и повторяющимися значениями, шаблон добавить \s* (0 или более пробелов) перед \1.
Подробности
(?<![^,]) - начало строки или любой символ кроме запятой([^,]+) - Группа 1: любой один или несколько символов, кроме запятой(?:,\1)+ - одна или несколько последовательностей запятой и значение в группе 1(?![^,]) - конец строки или символ, отличный от запятой.@Progrock Это выражение не генерируется автоматически, дело в том, что 1) вам нужна начальная граница, 2) повторяющийся шаблон внутри группы захвата, 3) группа с разделителем и обратная ссылка на группу 1, которая должна повторяться 1 или более раз, а затем 4) замыкающая граница. Без границ часть значения, зафиксированная в группе 1 могут быть сопоставлены ошибочно.
Итерируя с помощью 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);
(извините за получение и публикацию ошибочного вывода из вашего решения, поскольку ранее мне не удалось правильно скопировать это регулярное выражение.)