Regex para reemplazar todos los caracteres de tabulación iniciales, cada uno con un solo espacio [duplicado]

Nov 24 2020

Estoy buscando una expresión regular para reemplazar todos los caracteres de tabulación iniciales con un solo espacio (un espacio para cada carácter de tabulación inicial.

// input text with two leading tab characters and two tab characters elsewhere in the text
var input="     Some text       containing tabs";

// A:
console.log(input.replace(/\t/g, "LEADING_TAB_"));  
// prints: "LEADING_TAB_LEADING_TAB_Some textLEADING_TAB_LEADING_TAB_containing tabs"

// B:
console.log(input.replace(/\t/, "LEADING_TAB_"));  
// prints: "LEADING_TAB_   Some text       containing tabs"

// C:
console.log(input.replace(/^(\t)*/, "LEADING_TAB_"));  
// prints: "LEADING_TAB_Some text      containing tabs"

// D:
console.log(input.replace(/\t/gy, "LEADING_TAB_"));  
// prints: "LEADING_TAB_LEADING_TAB_Some text      containing tabs"

// E:
console.log(input.replace(/\t/y, "LEADING_TAB_"));  
// prints: "LEADING_TAB_   Some text       containing tabs"

Vea esto en un violín js: https://jsfiddle.net/onebcvu4/2/

La respuesta D funciona para mí.

input.replace(/\t/gy, " ")

Pero realmente no entiendo por qué. Especialmente porque, según la documentación de MDN , la bandera global (G) debe ignorarse cuando se usa con una bandera adhesiva.

Una expresión regular definida como permanente y global ignora la marca global.

¿Alguien puede claryfy o proporcionar otra solución que funcione?

Respuestas

2 T.J.Crowder Nov 24 2020 at 14:46

Su respuesta D funciona (y es bastante intuitivo) debido gy y no son excluyentes, sino que era razonable pensar que sería. Los detalles completos se encuentran en la especificación aquí y aquí , pero fundamentalmente las gmarcas se replacerepiten siempre que haya una coincidencia, y ysignifica que A) la expresión solo coincide en lastIndex(que por defecto es 0), y B) lastIndexno se actualiza. Así que coinciden en repetidas ocasiones una \ten lastIndexy volver a colocarlo cuando se le acaba el \tal lastIndex. Muy inteligente.

Si no quieres usar ese, también puedes hacerlo con una alternancia y una mirada positiva hacia atrás:

const result = input.replace(/(?:^\t|(?<=^\t*)\t)/g, " ");

Ejemplo en vivo:

const input = "\t\tSome text\t\tcontaining tabs";
const result = input.replace(/(?:^\t|(?<=^\t*)\t)/g, " ");

console.log(JSON.stringify(result));

O si está de acuerdo con pasar una devolución de llamada a replace, es más simple y no necesita mirar atrás (que es relativamente nuevo, ES2018): haga coincidir todos los \tcaracteres principales y reemplácelos con una cadena de espacios de la misma longitud:

const result = input.replace(/^(\t+)/, match => " ".repeat(match.length));

Ejemplo en vivo:

const input = "\t\tSome text\t\tcontaining tabs";
const result = input.replace(/^(\t+)/, match => " ".repeat(match.length));
console.log(JSON.stringify(result));