Как заменить только захваченные группы?


196

У меня есть HTML-код до и после строки:

name="some_text_0_some_text"

Я хотел бы заменить что- 0то вроде:!NEW_ID!

Итак, я сделал простое регулярное выражение:

.*name="\w+(\d+)\w+".*

Но я не вижу, как заменить исключительно захваченный блок.

Есть ли способ заменить захваченный результат, например ($ 1), какой-нибудь другой строкой?

Результат будет:

name="some_text_!NEW_ID!_some_text"

Ответы:


359

Решение состоит в том, чтобы добавить записи для предыдущего и следующего текста:

str.replace(/(.*name="\w+)(\d+)(\w+".*)/, "$1!NEW_ID!$3")

76
Привет из будущего! Ваше решение выглядит действительно аккуратно. Не могли бы вы объяснить свой ответ?
Полидукс

21
Круглые скобки используются для создания «групп», которым затем присваивается индекс base-1, доступный в замене на a $, поэтому первое слово (\w+)находится в группе и становится $1средней частью (\d+)второй группы ((но получает игнорируется при замене), а третья группа есть $3. Поэтому, когда вы задаете строку замены "$1!new_ID!$3", $ 1 и $ 3 автоматически заменяются первой группой и третьей группой, что позволяет заменить 2-ю группу новой строкой, сохраняя текст, окружающий ее.
mix3d

4
При этом, хотя я понимаю, КАК это работает, я надеялся на более элегантное решение>. <Тем не менее, я могу двигаться дальше со своим кодом сейчас!
mix3d

9
1) Вам даже не нужно захватывать \ d + 2) Почему вы говорите, что это не элегантно? Захват предназначен для того, чтобы хранить вещи, а не выбрасывать их. То, что вы хотите сохранить, это то, что есть вокруг \ d +, поэтому имеет смысл (и достаточно элегантно) захватить эти окружающие части.
Sir4ur0n

3
Хорошее решение. Что если мы хотим заменить группы захвата, используя группу захвата в качестве основы для преобразования? Есть ли такое же элегантное решение для этого? В настоящее время я сохраняю захваченные группы в списке, зацикливаю их и заменяю группу захвата преобразованным значением на каждой итерации
sookie

15

Теперь, когда у Javascript есть взгляд назад (по состоянию на ES2018 ), в более новых средах вы можете полностью избегать групп в подобных ситуациях. Скорее, посмотрите за тем, что предшествует группе, которую вы захватывали, и посмотрите вперед, и замените просто !NEW_ID! :

const str = 'name="some_text_0_some_text"';
console.log(
  str.replace(/(?<=name="\w+)\d+(?=\w+")/, '!NEW_ID!')
);

При использовании этого метода полное совпадение - это только та часть, которую необходимо заменить.

  • (?<=name="\w+)- Lookbehind for name", за которым следуют символы слова (к счастью, lookavhinds не обязательно должны иметь фиксированную ширину в Javascript!)
  • \d+ - Соответствие одной или нескольким цифрам - единственная часть шаблона, которая не находится в поиске, единственная часть строки, которая будет в полученном совпадении
  • (?=\w+")- Смотрим вперед для символов слова, за которыми следует " `

Имейте в виду, что взгляд сзади довольно новый. Он работает в современных версиях V8 (включая Chrome, Opera и Node), но не в большинстве других сред , по крайней мере, пока. Таким образом, хотя вы можете надежно использовать lookbehind в Node и в своем собственном браузере (если он работает в современной версии V8), он еще недостаточно поддерживается случайными клиентами (как на общедоступном веб-сайте).


Просто запустил тест на быстрое определение времени, и это довольно впечатляюще, как важны
Kaiido

Но если, например, я хочу извлечь число, кратное число и «положить его обратно», мне придется также группировать \d+, верно?
Mosh Feu

@MoshFeu Используйте функцию заменителя и используйте все совпадение, цифры: замените второй параметр на match => match * 2. Цифры по-прежнему полностью совпадают, поэтому нет необходимости в группах
CertainPerformance

Попался. Спасибо!
Mosh Feu

2

Небольшое улучшение в ответе Мэтью может быть предвкушением вместо последней группы захвата:

.replace(/(\w+)(\d+)(?=\w+)/, "$1!NEW_ID!");

Или вы можете разделить десятичную дробь и присоединиться к вашему новому идентификатору, например так:

.split(/\d+/).join("!NEW_ID!");

Пример / тест здесь: https://codepen.io/jogai/full/oyNXBX


1

С двумя группами захвата было бы также возможно; Я бы также добавил две черточки, как дополнительные левую и правую границы, до и после цифр, и измененное выражение выглядело бы так:

(.*name=".+_)\d+(_[^"]+".*)

const regex = /(.*name=".+_)\d+(_[^"]+".*)/g;
const str = `some_data_before name="some_text_0_some_text" and then some_data after`;
const subst = `$1!NEW_ID!$2`;
const result = str.replace(regex, subst);
console.log(result);


Если вы хотите изучить / упростить / изменить выражение, это было объяснено на верхней правой панели regex101.com . Если вы хотите, вы также можете посмотреть в этой ссылке , как она будет сопоставляться с некоторыми примерами входных данных.


RegEx Circuit

jex.im визуализирует регулярные выражения:

введите описание изображения здесь


0

Более простой вариант - просто захватить цифры и заменить их.

const name = 'preceding_text_0_following_text';
const matcher = /(\d+)/;

// Replace with whatever you would like
const newName = name.replace(matcher, 'NEW_STUFF');
console.log("Full replace", newName);

// Perform work on the match and replace using a function
// In this case increment it using an arrow function
const incrementedName = name.replace(matcher, (match) => ++match);
console.log("Increment", incrementedName);

Ресурсы

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.