¿Cómo reemplazar solo los grupos capturados?

196

Tengo código HTML antes y después de la cadena:

name="some_text_0_some_text"

Me gustaría reemplazar el 0 por algo como:!NEW_ID!

Entonces hice una expresión regular simple:

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

Pero no veo cómo reemplazar exclusivamente el bloque capturado.

¿Hay alguna manera de reemplazar un resultado capturado como ($ 1) por alguna otra cadena?

El resultado sería:

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

Respuestas:

359

Una solución es agregar capturas para el texto anterior y siguiente:

str.replace(/(.*name="\w+)(\d+)(\w+".*)/, "$1!NEW_ID!$3")
Matthew Flaschen
fuente
76
¡Saludos desde el futuro! Su solución se ve realmente ordenada. ¿Podría por favor explicar su respuesta?
Polyducks
21
Los paréntesis se usan para crear "grupos", a los que luego se les asigna un índice de base 1, accesible en un reemplazo con a $, por lo que la primera palabra (\w+)está en un grupo, y se convierte en $1, la parte central (\d+)es el segundo grupo, (pero obtiene ignorado en el reemplazo), y el tercer grupo es $3. Entonces, cuando asigna la cadena de reemplazo de "$1!new_ID!$3", $ 1 y $ 3 se reemplazan automáticamente con el primer grupo y el tercer grupo, lo que permite que el segundo grupo se reemplace con la nueva cadena, manteniendo el texto que lo rodea.
mix3d
44
Dicho esto, aunque entiendo CÓMO funciona, esperaba una solución más elegante>. <¡Sin embargo, ahora puedo avanzar con mi código!
mix3d
9
1) Ni siquiera necesita capturar \ d + 2) ¿Por qué dice que no es elegante? La captura está destinada a guardar cosas, no a tirarlas. Lo que desea mantener es lo que está ALREDEDOR \ d +, por lo que realmente tiene sentido (y es lo suficientemente elegante) para capturar estas partes circundantes.
Sir4ur0n
3
Buena solución ¿Qué sucede si queremos reemplazar los grupos de captura utilizando el grupo de captura como base para la transformación? ¿Existe una solución igualmente elegante para hacer esto? Actualmente almaceno los grupos capturados en una lista, los repito y reemplazo el grupo de captura con el valor transformado en cada iteración
sookie
15

Ahora que Javascript tiene retrospectiva (a partir de ES2018 ), en entornos más nuevos, puede evitar grupos por completo en situaciones como estas. Más bien, mire hacia atrás para lo que viene antes del grupo que estaba capturando, y busque para después, y reemplace con solo !NEW_ID! :

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

Con este método, la coincidencia completa es solo la parte que necesita ser reemplazada.

  • (?<=name="\w+)- Mire hacia atrás name", seguido de caracteres de palabras (¡afortunadamente, las miradas hacia atrás no tienen que tener un ancho fijo en Javascript!)
  • \d+ - Haga coincidir uno o más dígitos: la única parte del patrón que no está en una búsqueda, la única parte de la cadena que estará en la coincidencia resultante
  • (?=\w+")- Buscar caracteres de palabra seguidos de " `

Tenga en cuenta que mirar atrás es bastante nuevo. Funciona en versiones modernas de V8 (incluyendo Chrome, Opera y Node), pero no en la mayoría de los otros entornos , al menos todavía no. Por lo tanto, si bien puede usar mirar hacia atrás de manera confiable en Node y en su propio navegador (si se ejecuta en una versión moderna de V8), todavía no es lo suficientemente compatible con clientes aleatorios (como en un sitio web público).

Cierto rendimiento
fuente
Acabo de ejecutar una prueba de sincronización rápida, y es bastante impresionante cómo importa la entrada: jsfiddle.net/60neyop5
Kaiido
Pero si, por ejemplo, quiero extraer el número, múltiple y "volver a colocarlo", tendré que agruparlo también \d+, ¿verdad?
Mosh Feu
@MoshFeu Use una función de reemplazo y use toda la coincidencia, los dígitos: reemplace el segundo parámetro con match => match * 2. Los dígitos siguen siendo todo el partido, por lo que no hay necesidad de grupos
CertainPerformance
Te tengo. ¡Gracias!
Mosh Feu
2

Una pequeña mejora en la respuesta de Matthew podría ser una anticipación en lugar del último grupo de captura:

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

O podría dividirse en el decimal y unirse con su nueva identificación de esta manera:

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

Ejemplo / Benchmark aquí: https://codepen.io/jogai/full/oyNXBX

Jogai
fuente
1

Con dos grupos de captura también habría sido posible; También habría incluido dos guiones, como límites izquierdos y derechos adicionales, antes y después de los dígitos, y la expresión modificada se habría visto así:

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


Si desea explorar / simplificar / modificar la expresión, se explica en el panel superior derecho de regex101.com . Si lo desea, también puede ver en este enlace cómo coincidiría con algunas entradas de muestra.


Circuito RegEx

jex.im visualiza expresiones regulares:

ingrese la descripción de la imagen aquí

Emma
fuente
0

Una opción más simple es capturar los dígitos y reemplazarlos.

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
fuente