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

#php #regex

#php #регулярное выражение

Вопрос:

Как удалить последовательные повторяющиеся запятые в php. Попробовал этот фрагмент кода. Это работает, когда перед запятой нет пробела.

 <?php
$a="test, , 1, , , 245 Park Avenue, New York, NY";
$my_string = preg_replace("/, /", ",", $a);
$string=trim($my_string);
echo $string;
?>
 

ожидаемый вывод:

 $a="test, 1, 245 Park Avenue, New York, NY";
 

Ответ №1:

Просто измените свое регулярное выражение на это: (?:,s*){2,}

Вы можете протестировать это здесь: https://regex101.com/r/1vFhVb/2

Подробные сведения

  • ,s* означает запятую, за которой следуют некоторые пробелы или нет (также включает табуляцию).
  • (?:) это просто группа без захвата, потому что мы хотим ,s* несколько раз, поэтому мы помещаем ее в группу без захвата, подобную этой (?:,s*) , а затем мы можем добавить {2,} , чтобы сказать, что мы хотим это 2 или более раз.

Затем мы заменяем его на , (я добавил пробел для удобства чтения).

 $result = preg_replace(
  '/(?:,s*){2,}/',
  ', ',
  'test, , 1, , , 245 Park Avenue, New York, NY'
);

echo $resu<
 

отображает: test, 1, 245 Park Avenue, New York, NY

Редактировать (благодаря Маркусу)

Как упоминал @MarkusAO, если ваша строка многострочная, необходимо заменить s на h , который используется для сопоставления только горизонтальных пробелов. Это приведет к этому регулярному выражению: (?:,h*){2,}

Во-вторых, что нам делать с некоторыми запятыми в начале или в конце? Обычно как бы вы обрабатывали этот ввод?

 Av. de Lavaux, 31, , , 1009 Pully, VD,
, 45, 3rd floor, , 8000 Zürich, ZH, Switzerland
Test, 8, ,  41 77 800 80 80, , ,
, ,, the last test,, USA
 

Хотим ли мы сохранить конечную запятую в первой строке? Вероятно, нет. А запятые в начале? Может быть, не снова. В этом случае мы могли бы добавить некоторые регистры в регулярное выражение, чтобы удалить их:

  • Мы будем использовать многострочную опцию с m флагом. Это позволит нам сопоставлять начало строки с ^ и окончание строки с $ .
  • Мы будем использовать x флаг для расширенной нотации, чтобы мы могли поместить его в несколько строк для большей удобочитаемости. В этой ситуации пробелы и новые строки в регулярном выражении игнорируются.
  • Чтобы сопоставить запятые и пробелы в начале, это будет ^(?:h*,h*) . Поскольку мы на самом деле не знаем, есть ли пробелы до или после запятой, мы окружаем символ запятой символом h* . Тогда вся группа должна быть один или несколько раз, вот почему мы помещаем ее в группу без захвата с после нее.
  • Чтобы сопоставить запятые и пробелы в конце строки, это будет просто (?:h*,h*) $ . Это то же самое, что и выше, но вы ставите $ после повторяющихся запятых.
  • Теперь, чтобы убрать повторяющиеся запятые в середине, мы будем использовать другой подход. Раньше мы искали 2 или более запятых, чтобы заменить их на одну запятую. Теперь идея немного отличается, поскольку мы должны использовать пустую строку в строке замены для начальных и конечных запятых, обработанных выше. Поэтому мы будем убирать запятые только в том случае, если они стоят перед другой запятой. Это можно сделать с помощью позитивного взгляда:
    • Мы ищем запятую и несколько необязательных пробелов с ,h* помощью .
    • позитивный прогноз выполняется с использованием (?= ) синтаксиса. Мы хотим снова найти запятые и пробелы, поэтому мы поместим их внутрь, и это станет (?=,h*) .
    • собираем все вместе: ,h*(?=,h*)
  • Мы хотим сопоставить одно из 3 регулярных выражений. Это можно сделать с | помощью оператора. И если мы используем x флаг, упомянутый выше, мы можем написать его в несколько строк и добавить комментарии для удобства чтения:
     ^(?:h*,h*)   # Leading commas
    |
    (?:h*,h*) $  # Ending commas
    |
    ,h*(?=,h*)   # Commas followed by commas
     

Тестирование и воспроизведение: https://regex101.com/r/Gvu39h/4

PHP-код стал бы:

 <?php

$pattern = <<<REGEX
/
^(?:h*,h*)   # Leading commas
|
(?:h*,h*) $  # Ending commas
|
,h*(?=,h*)   # Commas followed by commas
/mx
REGEX;

$input = 'Av. de Lavaux, 31, , , 1009 Pully, VD,
, 45, 3rd floor, , 8000 Zürich, ZH, Switzerland
Test, 8, ,  41 77 800 80 80, , ,
, ,, the last test,, USA';

$replace = '';

echo preg_replace($pattern, $replace, $input);
 

Он выводит следующее:

 Av. de Lavaux, 31, 1009 Pully, VD
45, 3rd floor, 8000 Zürich, ZH, Switzerland
Test, 8,  41 77 800 80 80
the last test, USA
 

Комментарии:

1. В случае, если у нас есть промежуточный случай с начальными и конечными запятыми в многострочных CSV-данных (что может случиться), скажем, test,, foo, bar,n ,test,bar,,foo , s (любой пробел) превратит две строки в одну строку, поскольку он также соответствует вертикальным пробелам, используйте h только для горизонтального пробела. Пример: regex101.com/r/dqclnC/1

2. Привет @MarkusAO! Спасибо за эту замечательную идею. Я не думал о том, что ввод может быть многострочным. Я исправлю ответ вашим предложением.

3. Ну, вы превратили это в полноценный учебник по регулярным выражениям, не так ли, отличная работа! Что касается сохранения конечных / начальных запятых, кажется, что они будут иметь значение только с точки зрения структуры данных, о чем OP, похоже, не беспокоится.

Ответ №2:

Это может быть достигнуто с array_map помощью , array_filter и implode :

 <?php

$a = "test, , 1, , , 245 Park Avenue, New York, NY";

$explode = array_map(function ($e) {
    return trim($e);
}, explode(',', $a));

$filter = array_filter($explode);
$string = implode(', ', $filter);

echo $string . PHP_EOL;
 

ВОЗВРАТ test, 1, 245 Park Avenue, New York, NY

Комментарии:

1. Хорошая идея использовать explode / implode , но вместо обрезки с помощью array_map просто используйте explode с ', ' (запятая пробел) в качестве разделителя.

2. @CasimiretHippolyte У меня это было изначально, но проблема в том, что если есть последовательные запятые без пробела между ними, они не будут удалены

Ответ №3:

Если вы не хотите пересекать новые строки, которые могут быть сопоставлены s :

 ,h*,[h,]*
 
  • ,h*, Сопоставьте , необязательные горизонтальные символы пробелов и другие ,
  • [h,]* При необходимости повторите символьный класс, соответствующий , символу a или горизонтальному пробелу

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

 $a="test, , 1, , , 245 Park Avenue, New York, NY";
$my_string = preg_replace("/,h*,[h,]*/", ", ", $a);
echo $my_string;
 

Вывод

 test, 1, 245 Park Avenue, New York, NY
 

Ответ №4:

Вы можете использовать str_replace или explode и implode .

Если у вас есть строка like $a , то вы можете просто использовать str_replace .

 $a="test, , 1, , , 245 Park Avenue, New York, NY";
$my_string = str_replace(" ,","",$a);
$string=trim($my_string);
 

Но если у вас есть строка like $b , вы должны использовать implode и explode для создания новой строки. (Обратите внимание на запятые без начальных пробелов)

 $b = "test, , 1, , ,,, 245 Park Avenue, New York, NY";

$strArray = explode(',', $b);

$myArray = [];
foreach ($strArray as $str){

    if (trim($str) != null || trim($str) != ""){
        $myArray[] = $str;
    }

}

$myStr = implode(',', $myArray);
 

Ответ №5:

Возможно, это самое короткое возможное решение:

 while(($b = str_replace(', ,', ',', $a)) != $a) 
     $a = $b;
 

Но это работает, только если между запятыми есть пробелы.

Комментарии:

1. Вместо сравнения самой новой строки со старой, используйте str_replace с ее 4-м параметром в do...while цикле и поставьте счетчик в качестве условия while.