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

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), он еще недостаточно поддерживается случайными клиентами (как на общедоступном веб-сайте).

CertainPerformance
источник
Просто запустил тест на быстрое определение времени, и это довольно впечатляюще, как важны
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

Jogai
источник
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);

Ресурсы

CTS_AE
источник