Come sostituire solo i gruppi acquisiti?


196

Ho un codice HTML prima e dopo la stringa:

name="some_text_0_some_text"

Vorrei sostituire il 0con qualcosa del tipo:!NEW_ID!

Quindi ho creato una regex semplice:

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

Ma non vedo come sostituire esclusivamente il blocco acquisito.

C'è un modo per sostituire un risultato acquisito come ($ 1) con un'altra stringa?

Il risultato sarebbe:

name="some_text_!NEW_ID!_some_text"

Risposte:


359

Una soluzione consiste nell'aggiungere acquisizioni per il testo precedente e seguente:

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

76
Saluti dal futuro! La tua soluzione sembra davvero pulita. Potresti spiegare la tua risposta, per favore?
Polyducks,

21
Le parentesi vengono utilizzate per creare "gruppi", a cui viene assegnato un indice di base 1, accessibile in sostituzione con a $, quindi la prima parola (\w+)è in un gruppo e diventa $1, la parte centrale (\d+)è il secondo gruppo, (ma ottiene ignorato nella sostituzione), e il terzo gruppo è $3. Pertanto, quando si fornisce la stringa di sostituzione di "$1!new_ID!$3", $ 1 e $ 3 vengono sostituiti automaticamente con il primo gruppo e il terzo gruppo, consentendo al secondo gruppo di essere sostituito con la nuova stringa, mantenendo il testo che lo circonda.
mix3d

4
Detto questo, mentre capisco come funziona, speravo in una soluzione più elegante>. <Tuttavia, ora posso andare avanti con il mio codice!
mix3d

9
1) Non hai nemmeno bisogno di catturare \ d + 2) Perché dici che non è elegante? Catturare ha lo scopo di mantenere le cose, non buttarle via. Quello che vuoi mantenere è ciò che è AROUND \ d +, quindi ha davvero senso (ed è abbastanza elegante) catturare queste parti circostanti.
Sir4ur0n,

3
Bella soluzione. Che cosa succede se vogliamo sostituire i gruppi di acquisizione utilizzando il gruppo di acquisizione come base per la trasformazione? Esiste una soluzione altrettanto elegante per farlo? Attualmente memorizzo i gruppi acquisiti in un elenco, li
eseguo in

15

Ora che Javascript ha guardato indietro (a partire da ES2018 ), su ambienti più recenti, puoi evitare gruppi completamente in situazioni come queste. Piuttosto, cerca ciò che viene prima del gruppo che stavi catturando, e cerca dopo, e sostituisci con solo !NEW_ID! :

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

Con questo metodo, la corrispondenza completa è solo la parte che deve essere sostituita.

  • (?<=name="\w+)- Lookbehind for name", seguito da caratteri di parole (per fortuna, lookbehinds non deve essere fisso in Javascript!)
  • \d+ - Abbina una o più cifre: l'unica parte del motivo non presente in un lookaround, l'unica parte della stringa che sarà nella corrispondenza risultante
  • (?=\w+")- Lookahead per i caratteri delle parole seguiti da " `

Tieni presente che lookbehind è piuttosto nuovo. Funziona nelle versioni moderne di V8 (inclusi Chrome, Opera e Node), ma non nella maggior parte degli altri ambienti , almeno non ancora. Quindi, sebbene sia possibile utilizzare in modo affidabile lookbehind in Node e nel proprio browser (se funziona su una versione moderna di V8), non è ancora sufficientemente supportato da client casuali (come su un sito Web pubblico).


Ho appena eseguito un rapido test di temporizzazione ed è abbastanza impressionante come l'immissione sia importante: jsfiddle.net/60neyop5
Kaiido

Ma se, per esempio, voglio estrarre il numero, multiplo e "rimetterlo", dovrò raggruppare anche io \d+, giusto?
Mosh Feu

@MoshFeu Utilizza una funzione di sostituzione e usa l'intera corrispondenza, le cifre: sostituisci il secondo parametro con match => match * 2. Le cifre sono ancora l'intera partita, quindi non c'è bisogno di gruppi
CertainPerformance

Ti ho preso. Grazie!
Mosh Feu

2

Un piccolo miglioramento alla risposta di Matthew potrebbe essere uno sguardo al posto dell'ultimo gruppo di acquisizione:

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

Oppure potresti dividere il decimale e unirti al tuo nuovo ID in questo modo:

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

Esempio / benchmark qui: https://codepen.io/jogai/full/oyNXBX


1

Con due gruppi di cattura sarebbe stato anche possibile; Avrei anche incluso due trattini, come ulteriori limiti sinistro e destro, prima e dopo le cifre, e l'espressione modificata sarebbe stata simile:

(.*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 desideri esplorare / semplificare / modificare l'espressione, è stata spiegata nel pannello in alto a destra di regex101.com . Se lo desideri, puoi anche guardare in questo link , come corrisponderebbe ad alcuni input di esempio.


Circuito RegEx

jex.im visualizza le espressioni regolari:

inserisci qui la descrizione dell'immagine


0

Un'opzione più semplice è semplicemente catturare le cifre e sostituirle.

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

risorse

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.