2011-12-11 20 views
5

Quiero transformar el siguiente textoSustitución de expresiones regulares parcial coincide en su lugar con Ruby

This is a ![foto](foto.jpeg), here is another ![foto](foto.png) 

en

This is a ![foto](/folder1/foto.jpeg), here is another ![foto](/folder2/foto.png) 

En otras palabras que quiero encontrar todas las rutas de las imágenes que se incluyen entre paréntesis (el texto está en la sintaxis de Markdown) y los reemplaza con otras rutas. La cadena que contiene la nueva ruta es devuelta por una función separada real_path.

Me gustaría hacer esto usando String#gsub en su versión de bloque. Actualmente mi código es el siguiente:

re = /!\[.*?\]\((.*?)\)/ 

rel_content = content.gsub(re) do |path| 
    real_path(path) 
end 

El problema con esto es que la expresión regular coincidirá con ![foto](foto.jpeg) en lugar de sólo foto.jpeg. También probé otras expresiones regulares como (?>\!\[.*?\]\()(.*?)(?>\)) pero fue en vano.

Mi solución actual es dividir la ruta y volver a montarla más tarde.

¿Hay una expresión regular de Ruby que coincida solo con la ruta dentro de los corchetes y no con todos los caracteres contextuales necesarios?

Actualización posterior a la respuesta: El principal problema aquí es que las expresiones regulares de Ruby no tienen forma de especificar el ancho cero detrás de ellas. La solución más genérica es agrupar lo que es la parte de regexp anterior y la que está después de la parte de coincidencia real, es decir, /(pre)(matching-part)(post)/, y luego reconstruir la cadena completa.

En este caso la solución sería

re = /(!\[.*?\]\()(.*?)(\))/ 

rel_content = content.gsub(re) do 
    $1 + real_path($2) + $3 
end 

Respuesta

5

Una solución rápida (ajuste si es necesario):

s = 'This is a ![foto](foto.jpeg)' 

s.sub!(/!(\[.*?\])\((.*?)\)/, '\1(/folder1/\2)') 

p s # This is a [foto](/folder1/foto.jpeg) 
+0

Tal vez la mejor idea es, como usted sugiere, para salvar la pieza antes y después del partido real en grupos separados y reconstruir la cadena final con '$ 1 + ruta_real ($ 2) + $ 3'. – gioele

3

En su bloque, utilice $1 para acceder al primer grupo de captura ($2 para el segundo y así sucesivamente).

De la documentación:

En la forma de bloques, la cadena de coincidencia corriente se pasa como un parámetro, y las variables tales como $ 1, $ 2, $ ', $ &, y $' se establecerá adecuadamente. El valor devuelto por el bloque se sustituirá por la coincidencia en cada llamada.

4

Siempre puede hacerlo en dos pasos - primero extraer toda la expresión de imagen y luego vuelva a colocar la segunda el enlace:

str = "This is a ![foto](foto.jpeg), here is another ![foto](foto.png)" 

str.gsub(/\!\[[^\]]*\]\(([^)]*)\)/) do |image| 
    image.gsub(/(?<=\()(.*)(?=\))/) do |link| 
    "https://stackoverflow.com/a/new/path/" + link 
    end 
end 

#=> "This is a ![foto](/a/new/path/foto.jpeg), here is another ![foto](/a/new/path/foto.png)" 

Cambié un poco la primera expresión regular, pero puede usar la misma que tenía antes en su lugar. image es la expresión de la imagen como ![foto](foto.jpeg), y link es solo la ruta como foto.jpeg.

[EDIT] Aclaración: Rubí tiene lookbehinds (y que se utilizan en mi respuesta):

Puede crear lookbehinds con (?<=regex) para positivo y (?<!regex) para el negativo, donde regex es una expresión regular arbitraria expresión sujeta a la siguiente condición. Las expresiones Regexp en lookbehinds tienen que ser de ancho fijo debido a limitaciones en la implementación de expresiones regulares, lo que significa que no pueden incluir expresiones con un número desconocido de repeticiones o alternancias con opciones de ancho diferente. Si intentas hacer eso, obtendrás un error. (La restricción no se aplica a lookaheads, sin embargo).

En su caso, la parte [foto] tiene un ancho variable (foto puede ser cualquier cadena) por lo que no puede entrar en un lookbehind debido a lo anterior. Sin embargo, mirar hacia atrás es exactamente lo que necesitamos ya que es una coincidencia de ancho cero, y lo aprovechamos en la segunda expresión regular, que solo tiene que preocuparse acerca de los paréntesis obligatorios de apertura (longitud fija).

Obviamente puedes poner real_path desde aquí, pero solo quería un ejemplo de prueba.

creo que este enfoque es más flexible y más fácil de leer que la reconstrucción de la cadena a través de las variables de grupo partido

+0

Esto es lo que estoy haciendo ahora. Esperaba que hubiera alguna sintaxis que no conocía para crear miradas atrás en Ruby's Regexps :(. – gioele

+0

Puedes mirar atrás en rubí, pero hay una condición. Respuesta actualizada para explicar mejor – arcresu

Cuestiones relacionadas