Замените каждый экземпляр между двумя символами
У меня есть следующие данные ниже, где {n}
представляет собой заполнитель.
{n}{n}A{n}{n}A{n}
{n}A{n}{n}{n}{n}A
{n}{n}A{n}A{n}{n}
{n}{n}{n}A{n}A{n}B
{n}A{n}{n}B{n}{n}
A{n}B{n}{n}{n}{n}
Я хотел бы заменить каждый экземпляр заполнителя между двумя символами A, например, буквой C
. Я написал для этого следующее регулярное выражение и использую функцию preg_replace
.
$str = preg_replace('~(?<=A)(\{n\})*(?=A)~', 'C', $str);
Проблема в том, что он заменяет все экземпляры между двумя A на один C
. Как я мог бы исправить свое регулярное выражение или вызов preg_replace
, чтобы заменить каждый отдельный экземпляр заполнителей на C
?
Это должно быть моим результатом.
{n}{n}ACCA{n}
{n}ACCCCA
{n}{n}ACA{n}{n}
{n}{n}{n}ACA{n}B
{n}A{n}{n}B{n}{n}
A{n}B{n}{n}{n}{n}
Но в настоящее время он выводит это.
{n}{n}ACA{n}
{n}ACA
{n}{n}ACA{n}{n}
{n}{n}{n}ACA{n}B
{n}A{n}{n}B{n}{n}
A{n}B{n}{n}{n}{n}
3 answers
Вы можете решить проблему, установив якорь с помощью \G
.
$str = preg_replace('~(?:\G(?!\A)|({n})*A(?=(?1)++A))\K{n}~', 'C', $str);
Функция \G
- это якорь, который может совпадать в одной из двух позиций; начало позиции строки или позиция в конце последнего совпадения. Escape-последовательность \K
сбрасывает начальную точку сообщенного совпадения, и все ранее использованные символы больше не включаются.
Чтобы уменьшить количество возвратов, вы могли бы использовать более сложное выражение:
$str = preg_replace('~\G(?!\A)(?:{n}
|A(?:[^A]*A)+?((?=(?:{n})++A)\K{n}
|(*COMMIT)(*F)))
|[^A]*A(?:[^A]*A)*?(?1)~x', 'C', $str);
Несколько более подробное, но более простое в использовании решение состоит в том, чтобы использовать начальное выражение для разбиения текста на группы; затем примените индивидуальное преобразование внутри каждой группы:
$text = preg_replace_callback('~(?<=A)(?:\{n\})*(?=A)~', function($match) {
// simple replacement inside
return str_replace('{n}', 'C', $match[0]);
}, $text);
Я немного изменил выражение, чтобы избавиться от захвата памяти, в котором нет необходимости, с помощью (?:...)
.
(?<=A){n}(?=(?:{n})*A)|\G(?!^){n}
Вы можете попробовать это. Заменить на C
. Здесь вы должны использовать \G
, чтобы утвердить позицию в конце предыдущего совпадения или в начале строки для первого совпадения.
Чтобы вы могли соответствовать после вашего первого матча. Смотрите демонстрацию.
Https://regex101.com/r/wU4xK1/7
Здесь сначала вы сопоставляете {n}
, за которым стоит A
, и A
после него, который может иметь {n}
между ними. После захвата вы используете \G
для сброса до конца предыдущего матча и впоследствии продолжайте заменять {n}
найденный.
$re = "/(?<=A){n}(?=(?:{n})*A)|\\G(?!^){n}/";
$str = "{n}{n}A{n}{n}A{n}\n{n}A{n}{n}{n}{n}A\n{n}{n}A{n}A{n}{n}\n{n}{n}{n}A{n}A{n}B\n{n}A{n}{n}B{n}{n}\nA{n}B{n}{n}{n}{n}";
$subst = "C";
$result = preg_replace($re, $subst, $str);