substitute – Cómo ejecutar un comando sustituto solo en una determinada parte de la línea

Pregunta:

Tengo la siguiente línea en mi código:

INCORRECT_EMAIL_MOBILE_COMBINATION("incorrect_email_mobile_combination");

Cuando trato de sustituir los guiones bajos con espacios en modo visual (usando :'<,'>s/_/ /g ), vim sustituye todos los guiones bajos en la línea.

¿Cómo reemplazo los guiones bajos entre las comillas?

Respuesta:

Sí, esta es una respuesta larga. Eso es porque trato de cubrir la expresión de sub-reemplazo a veces intimidante tan claramente como puedo. No tengas miedo … ¡sumérgete! 🙂

Esto puede verse como una búsqueda-reemplazo donde la búsqueda es multinivel o anidada. Es decir, primero debe hacer coincidir parte de la línea (es decir, texto entre comillas en este caso). Luego, debe hacer coincidir parte de esa cadena (es decir, guiones bajos) y hacer el reemplazo en eso.

Las expresiones regulares básicas no estaban destinadas a manejar búsquedas anidadas (dependiendo de la variante de expresiones regulares, puede ser posible una solución compleja). Afortunadamente, Vim tiene algo llamado expresión de sub-reemplazo y se usa comúnmente para resolver estos problemas. Así es como lo aplicaríamos al ejemplo de la pregunta:

:s/"\zs[^"]\+\ze"/\=substitute(submatch(0), '_', ' ', 'g')/

En el componente de patrón, hacemos coincidir cadenas entre comillas y luego segregamos el texto dentro de las comillas con el par \zs\ze . Entonces, el patrón coincidirá solo con la cadena dentro de las comillas dobles. Rompiéndolo …

  • " : coincide con una comilla doble de apertura
  • \zs : excluir de la coincidencia final (a la que se aplica la sustitución) todo el texto anterior
  • [^"]\+ : coincide con uno o más caracteres excluyendo las comillas dobles
  • \ze : excluir de la coincidencia final todo el texto siguiente
  • " : coincide con una comilla doble (de cierre)

En el componente de reemplazo abrimos con \= . Este operador especial le dice a Vim que trate todo lo que sigue como una expresión, lo evalúe y use el resultado como reemplazo del texto coincidente final .

En la expresión invocamos substitute() . Los parámetros de esta función son:

substitute({expression}, {pattern}, {replacement}, {flags})

Los parámetros 2-4 reflejan los componentes de un comando de sustitución, es decir :s/pattern/replacement/flags . El primer parámetro debe resolverse en la cadena a la que se aplican los demás parámetros. Estamos usando submatch({matchnum}) que, con el valor de parámetro 0, devuelve la cadena coincidente completa del patrón descrito anteriormente. (Los valores distintos de 0 devolverán, naturalmente, subcoincidencias de patrón que se denotan por \( y \) circundante. No tenemos subcoincidencias aquí, pero a continuación hay un ejemplo que las usa).

En este caso, el valor de retorno y, por lo tanto, el texto de reemplazo es justo lo que estamos buscando: la cadena entre las comillas espera con espacios en lugar de guiones bajos.


¿Traer un lanzacohetes a una pelea con cuchillos?

Algunas personas han opinado que este enfoque es demasiado complicado. Si bien no lo usaría para la edición única de una o dos líneas, respetuosamente, la geometría no euclidiana es complicada. Para esto, todo lo que necesita es una familiaridad razonable con las expresiones regulares y \zs .. \ze para que coincida con la (s) parte (s) de la línea a la que desea aplicar la sustitución. Entonces…

  1. Escribe el patrón
  2. Escribe o pega esto: /\=substitute(submatch(0), '', '', 'g')/
  3. Complete los parámetros 2 y 3 con texto de reemplazo y reemplazo

Material adicional

Dado que parece haber un poco de interés en torno a este tema, pensé que agregaría un par de variaciones de la solución anterior con la esperanza de arrojar más luz sobre las cosas. A todos nos gustan muchos ejemplos, ¿verdad?

Resolviendo sin \ zs o \ ze

En la solución original, uso el par de tokens \zs .. \ze específico de Vim porque saber cómo usarlos brinda mucha flexibilidad (con respecto a las expresiones de sub-reemplazo y, en general, cuando se usa regex en Vim). En realidad, aunque el caso específico de los OP no los necesita …

 s/"[^"]\+"/\=substitute(submatch(0), '_', ' ', 'g')/

Solo incluimos las comillas en la cadena coincidente y está bien porque la sustitución solo afecta a los guiones bajos.

Un problema un poco más difícil

Nuestra cadena de entrada es

INCORRECT_EMAIL_MOBILE_COMBINATION("_incorrect_email_mobile_combination_");

… y queremos reemplazar los guiones bajos dentro de las comillas dobles, pero solo aquellos que tienen caracteres de palabra en ambos lados. Entonces queremos terminar con …

INCORRECT_EMAIL_MOBILE_COMBINATION("_incorrect email mobile combination_");

Sí, \zs .. \ze volvería a ser útil aquí, pero supongamos que necesitamos una expresión regular portátil …

Solución:

s/"_\([^"]\+\)_"/\='"_' . substitute(submatch(1), '_', ' ', 'g') . '_"'/

Primero, envolvemos la parte de la cadena a modificar en un grupo de captura \(..\) . Esto nos permite aislar la cadena de las comillas y guiones bajos externos cuando hacemos la sustitución usando submatch(1) (devuelve la cadena contenida en el primer grupo de captura) en lugar de submatch(0) (devuelve la cadena completa coincidente).

Por supuesto, perderemos los pares de comillas y subrayados si nos detenemos en la llamada substitute() . Pero podemos simplemente insertarlos en la expresión de sub-reemplazo literalmente citando (simple) y usando el operador de concatenación ( . ) … problema resuelto. Está bien, pero ¿y si los personajes circundantes no se corrigieron? Digamos que las comillas pueden ser simples o dobles, ['"]_ .. _['"]

Luego, tendremos que usar más grupos de captura y cambiar la cadena literal a llamadas submatch() con índices apropiados.

Cambiar al modo muy mágico ( \v ) para que el patrón sea un poco más fácil de leer, nuestra solución es …

s/\v(['"]_)([^"]+)(_['"])/\=submatch(1) . substitute(submatch(2), '_', ' ', 'g') . submatch(3)/

Leave a Comment

Your email address will not be published.

Scroll to Top

istanbul avukat

-

web tasarım