Grupo coincidente anidado en expresiones regulares


8

Tengo un caso de uso común cuando transformo alguna expresión de Python de la siguiente manera:

value 1
value 2
value 3

dentro

['value 1', 'value 2', 'value 3']

La forma más fácil puede ser usar un mapeo, pero quería usar una sustitución para esta tarea.

Hasta ahora tengo:

s/\(.*\n\)\+/[&]/g

¿Qué resultado en

[value 1
value 2
value 3
]

Esto plantea una pregunta, porque quiero poder hacer coincidir el \(.*\), pero no el \ny usar el resultado de la coincidencia dentro de a '...'.

Sabes como hacer esto?


2
No sé cómo hacerlo en una sola sustitución, pero podría hacerlo en 2 mientras está en modo visual (después de seleccionar la expresión de Python): :'<,'>s/\v(.*)\n/'\1', / | s/\v(.*), /[\1]/podría transformar esto en un mapeo visual: xnoremap ,x :s/\v(.*)\n/'\1', / <Bar> s/\v(.*), /[\1]/<CR>y tal vez en un mapeo normal si el la expresión está dentro de un párrafo: nnoremap ,x :'{+1,'}-1s/\v(.*)\n/'\1', / <Bar> s/\v(.*), /[\1]/<CR>Aquí estaría el mapeo ,x.
user9433424

1
no podía hacer con expresiones regulares, pero usando comandos externos:%! echo "[$(sed "s/.*/'&',/" % | tr '\n' ' ' | sed 's/, $//')]"
Sundeep

Respuestas:


5

Editar

Es posible hacer esto en una expresión si usamos una "expresión de sub-reemplazo". Ver abajo para obtener información sobre eso.

/Editar

El problema aquí es que quieres hacer dos cosas diferentes.

  1. Operar en el partido como un todo (es decir, rodearlo con [])

  2. Operar en cada elemento del partido (es decir, rodearlos con '',)

Puedes hacer cualquiera de los dos fácilmente:

  1. :s/\(.\+\n\)\+/[&]/
  2. :%s/\(.\+\)\n/'\1', /

pero que yo sepa no hay forma de hacer ambas cosas en una sola operación. Intenté obtener el resultado adecuado con algo como:

:s/\(\(.\+\)\n\)\+/[\2]/

Pero, por supuesto, el problema con esto es que \2solo coincide con la última coincidencia del segundo conjunto de paréntesis de memoria \(\)y no "recuerda" nada antes de eso. Entonces terminas solo con la última línea.

Recomendaría hacer un procesamiento previo / posterior con un :s///comando adicional para deshacerse de las nuevas líneas antes / después del hecho. Esto es lo que se me ocurrió

function! FormatExpression()
   .,/\n^$/s/\(.*\)\n/'\1', /
   s/\(.*\), /[\1]/
endfunction

Primera línea (eliminar líneas nuevas)

  • .,/\n^$/Este es un modificador de rango para la búsqueda y reemplazo. Sin esto, el comando continuará para mutilar todo su archivo. Actualmente va de la línea actual .a la siguiente línea en blanco \n^$. No estoy seguro de cómo pretendía dividir las cosas, pero necesita alguna forma de decirle que se detenga.
  • s/ El comienzo de un comando de búsqueda y reemplazo
  • \(.*\)\n Haga coincidir toda la línea, pero solo guarde la parte sin la nueva línea.
  • '\1', Reemplace la línea con la coincidencia entre comillas simples y agregue una coma.

2da línea (envolvente entre paréntesis)

  • \(.*\), Une toda la línea pero no la última coma y espacio
  • [\1] Rodea con corchetes y también elimina comas y espacios finales superfluos.

Voy a seguir investigando esto, pero por el momento no creo que sea posible con una sola expresión. :(

EDITAR:

¡He encontrado una manera de hacer esto con una sola expresión! Internamente, esto es en realidad dos sustituciones, pero técnicamente es una expresión. Esto es lo que se me ocurrió:

:s/\v((.+\n)*.+)\n/\= "['" . substitute(submatch(1), '\n', "', '", 'g') . "']" /
  • :s///: Hacer una sustitución
  • \v((.+\n)*.+)\n: Básicamente, reúne todas las siguientes líneas que no están en blanco y las almacena, excepto la última. \n
  • \=Nos permite usar una expresión en el reemplazo (ver :h sub-replace-expression)
  • substitute(submatch(1)...): Reemplaza todos los almacenados \ncon', '
  • "['" . ... . "']": Antecede ['y anexa']

Esto comenzará en la posición del cursor y continuará hasta que encuentre una línea en blanco ( ^\n). No tomar el último \nes importante ya que sin ese bit nos queda un adicional ',que no queremos al final.

Algunos podrían considerar esto más complejo que la respuesta anterior de dos expresiones. Pero pensé en seguir adelante y agregar esto, ya que de hecho es posible hacerlo con una sola expresión. :)


2

Destacar visualmente, luego:

:'<,'> s/.*/['&']/ | *j! | s/]\[/, /ge

Rodea cada línea, para hacer ['value 1'], por ejemplo , las une a todas, luego las reemplaza adyacentes ]y [con comas.

La documentación para el *en *j!se encuentra en :help cpo-star, por cierto. Eso es un poco difícil de encontrar.


Niza en torno al trabajo :)
nobe4

En realidad, puede usar :'<,'>s/\v(.*)(\_.)/['\1']/y eliminar la unión.
nobe4

Sí, pero se come la final \n, por eso lo usé :join. Probablemente debería haber mencionado eso. :-)
Antony

1
¿Qué tal '<,'>s/.*/['&']/ | *s/]\_.\[/, /entonces?
nobe4

1
Si, eso está mejor. Aunque probablemente escribiría la segunda parte como *s/]\n\[/, /e.
Antony
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.