Como substituir apenas grupos capturados?

193

Eu tenho código HTML antes e depois da string:

name="some_text_0_some_text"

Gostaria de substituir o 0por algo como:!NEW_ID!

Então eu fiz um regex simples:

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

Mas não vejo como substituir exclusivamente o bloco capturado.

Existe uma maneira de substituir um resultado capturado como ($ 1) por alguma outra string?

O resultado seria:

name="some_text_!NEW_ID!_some_text"
Nicolas Guillaume
fonte

Respostas:

358

Uma solução é adicionar capturas para o texto anterior e seguinte:

str.replace(/(.*name="\w+)(\d+)(\w+".*)/, "$1!NEW_ID!$3")
Matthew Flaschen
fonte
76
Saudações do futuro! Sua solução parece realmente limpa. Poderia explicar sua resposta?
Polyducks
21
Os parênteses são usados ​​para criar "grupos", os quais recebem um índice de base 1, acessível em uma substituição por a $, de modo que a primeira palavra (\w+)está em um grupo e se torna $1, a parte do meio (\d+)é o segundo grupo (mas obtém ignorado na substituição) e o terceiro grupo é $3. Portanto, quando você fornece a sequência de substituição de "$1!new_ID!$3", $ 1 e $ 3 são substituídos automaticamente pelo primeiro e pelo terceiro grupo, permitindo que o segundo grupo seja substituído pela nova sequência, mantendo o texto ao seu redor.
mix3d
4
Dito isto, enquanto eu entendo COMO funciona, eu esperava uma solução mais elegante>. <No entanto, agora posso avançar com meu código!
mix3d
9
1) Você nem precisa capturar \ d + 2) Por que você diz que não é elegante? Capturar é para guardar coisas, não jogá-las fora. O que você deseja manter é o que é AROUND \ d +, por isso realmente faz sentido (e é suficientemente elegante) para capturar essas partes circundantes.
Sir4ur0n
3
Ótima solução. E se quisermos substituir os grupos de captura usando o grupo de captura como base para a transformação? Existe uma solução igualmente elegante para fazer isso? Atualmente eu armazenar os grupos capturados em uma lista, ciclo-los, e substituir o grupo de captura com o valor transformado a cada iteração
Sookie
15

Agora que o Javascript olhou para trás (a partir do ES2018 ), em ambientes mais novos, é possível evitar grupos inteiramente em situações como estas. Em vez disso, preste atenção no que vem antes do grupo que você estava capturando, e procure logo depois, e substitua por apenas !NEW_ID! :

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

Com esse método, a correspondência completa é apenas a parte que precisa ser substituída.

  • (?<=name="\w+)- Lookbehind name", seguido por caracteres de palavra (felizmente, lookbehinds não precisa ter largura fixa em Javascript!)
  • \d+ - Corresponde a um ou mais dígitos - a única parte do padrão que não está em uma visão geral, a única parte da sequência que estará na correspondência resultante
  • (?=\w+")- Procure por caracteres de palavras seguidos por " `

Lembre-se de que olhar para trás é muito novo. Ele funciona nas versões modernas do V8 (incluindo Chrome, Opera e Node), mas não na maioria dos outros ambientes , pelo menos ainda não. Portanto, embora você possa usar o lookbehind de forma confiável no Node e em seu próprio navegador (se ele for executado em uma versão moderna do V8), ele ainda não é suficientemente suportado por clientes aleatórios (como em um site público).

CertainPerformance
fonte
Apenas executou um teste de sincronismo rápida, e é bastante impressionante como a entrada importa: jsfiddle.net/60neyop5
Kaiido
Mas se, por exemplo, eu quero extrair o número, múltiplo e "colocá-lo de volta", terei que agrupar também \d+, certo?
Mosh Feu
@MoshFeu Use uma função substituta e use a correspondência inteira, os dígitos: substitua o segundo parâmetro por match => match * 2. Os dígitos ainda são a partida inteira, então não há necessidade de grupos
CertainPerformance
Entendi. Obrigado!
Mosh Feu
2

Um pequeno aprimoramento na resposta de Matthew poderia ser um olhar atento, em vez do último grupo de captura:

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

Ou você pode dividir no decimal e associar-se ao seu novo ID assim:

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

Exemplo / Referência aqui: https://codepen.io/jogai/full/oyNXBX

Jogai
fonte
1

Com dois grupos de captura também teria sido possível; Eu também incluiria dois traços, como limites esquerdo e direito adicionais, antes e depois dos dígitos, e a expressão modificada teria a seguinte aparência:

(.*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);


Se você deseja explorar / simplificar / modificar a expressão, isso foi explicado no painel superior direito de regex101.com . Se desejar, também é possível assistir neste link , como ele corresponderia a algumas entradas de amostra.


Circuito RegEx

O jex.im visualiza expressões regulares:

insira a descrição da imagem aqui

Emma
fonte
0

Uma opção mais simples é capturar os dígitos e substituí-los.

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);

Recursos

CTS_AE
fonte