2009-01-20 20 views
8

He escrito un sistema de gestión de contenido que utiliza una expresión regular del lado del servidor para escapar símbolos en la respuesta de la página justo antes de que se envíe al navegador del cliente. La expresión regular es consciente de los signos y signos que ya se han escapado o son parte de una entidad HTML. Por ejemplo, los siguientes:Expresión regular para escaparse símbolos HTML respetando CDATA

a & b, c & d, © 2009

se cambia a esto:

a & b, c & d, © 2009

(Sólo se modifica el primer &.) Aquí es la expresión regular, que fue tomada y modificada a partir de un ayudante Rieles:

html.gsub(/&(?!([a-zA-Z][a-zA-Z0-9]*|(#\d+));)/) { |special| ERB::Util::HTML_ESCAPE[special] } 

Si bien esto funciona muy bien, tiene un problema. La expresión regular no tiene conocimiento de ningún <![CDATA[ o ]]> que pueda estar rodeando a los signos y símbolos sin guardar. Esto es necesario para que el JavaScript incrustado permanezca intacto. Por ejemplo, esto:

<script type="text/javascript"> 
    // <![CDATA[ 
    if (a && b) doSomething(); 
    // ]]> 
</script> 

Sentimos prestados como esto:

<script type="text/javascript"> 
    // <![CDATA[ 
    if (a &amp;&amp; b) doSomething(); 
    // ]]> 
</script> 

que por supuesto los motores de JavaScript no entienden.

Mi pregunta es esta: ¿Hay alguna forma de modificar la expresión regular para que haga exactamente lo mismo que ahora, con la excepción de que deja intacto el texto dentro de una sección CDATA?

Dado que la expresión regular no es tan simple para empezar, esta pregunta podría ser más fácil de responder: ¿Es posible escribir una expresión regular que cambie todas las letras en un período excepto esas letras entre '<' y '>'? Por ejemplo, uno que cambiaría "some <words> are < safe! >" en ".... <words> ... < safe! >"?

+0

Me sorprendería si esto pudiera resolverse usando expresiones regulares solamente, entonces estoy más ansioso de ver a alguien responder esta pregunta :-) –

+0

¿Cómo mostraría un usuario la cadena actual '&' si quisieran ? (por ejemplo, en una muestra de HTML) – orip

Respuesta

7

¡Lo pediste! : D

/&(?!(?:[a-zA-Z][a-zA-Z0-9]*|#\d+);) 
(?!(?>(?:(?!<!\[CDATA\[|\]\]>).)*)\]\]>)/xm 

La primera línea es su expresión regular original.El lookahead coincide si hay una secuencia de cierre de CDATA (]]>) más adelante, a menos que haya una secuencia de apertura (<!CDATA[) entre aquí y allá. Suponiendo que el documento está formado mínimamente, eso debería significar que la posición actual está dentro de una sección CDATA.

Vaya, lo tenía al revés: mediante el uso de una mirada positiva hacia delante estaba emparejando símbolos "desnudos" solo dentro de las secciones de CDATA. Lo cambié a un aspecto negativo, así que ahora funciona bien.

Por cierto, esta expresión regular funciona en RegexBuddy en modo Ruby, pero no en the rubular site. Sospecho que Rubular usa una versión anterior de Ruby con soporte de expresiones regulares menos poderoso; ¿Alguien puede confirmar eso? (Como ya habrás adivinado, no soy un programador de Ruby.)

EDITAR: El problema en Rubular era que usaba 's' como modificador (para referirme a las coincidencias de puntos), pero Ruby usa ' m 'por eso.

+0

Buena solución. Esto me tomó bastante tiempo para asimilar. Aquí hay una explicación detallada si alguien más está interesado: http://bitkickers.blogspot.com/2009/01/regular-expression-negative-lookahead_31.html –

+1

"Creo que esto se explica por sí mismo. ¡Nos vemos la próxima vez!" : D –

0

que he hecho algo similar aquí:
Best way to encode text data for XML

Afortunadamente, en mi caso CDATA no era un problema.

Qué es un problema es que usted tiene que tener cuidado de que la expresión no es codicioso o que va a terminar con algo como esto:

.... <words> are < safe! >

0

tengo serias dudas de que lo que está tratando para lograr es algo que puedes hacer usando solo una expresión regular. Regexps son notoriamente malos en la correcta entrega de anidación.

Probablemente sea mejor que utilice un analizador XML y no escape del contenido CDATA.

3

No use expresiones regulares para esto. Es una idea terrible y terrible. En su lugar, simplemente HTML codifica todo lo que está produciendo que podría tener un carácter. De esta manera:

require 'cgi' 
print CGI.escape("All of this is HTML encoded!") 
+0

¿Esto no causaría que las entidades ya escapadas se codifiquen doblemente? (por ejemplo, '&' -> '& amp;'?) –

+1

No quiero escapar de todo por algunas razones, una de ellas es que (como dijo Ben Blank) & se convertiría en & amp; pero también porque no quiero que se escapen caracteres en JavaScript en línea, de ahí la necesidad de excluir secciones de CDATA. – Nick

+0

Whoops. Debería haber dicho unescape en su lugar. –

1

¡Eso funcionó! En Rubular tuve que cambiar las opciones de /xs a /m (y eliminé el espacio en blanco que separa las dos partes de la expresión regular como lo mostraste arriba).

Puede ver esta expresión regular en acción junto con una cadena de muestra en http://www.rubular.com/regexes/5855.

En caso de que Rubular enlace permanente no es realmente permanente, esto es lo que ha introducido para la expresión regular:

/&(?!(?:[a-zA-Z][a-zA-Z0-9]*|#\d+);)(?!(?>(?:(?!<!\[CDATA\[|\]\]>).)*)\]\]>)/m 

Y aquí es la cadena de prueba:

<p>a & b</p> 
<p>c &amp; d</p> 
<script type="text/javascript"> 
    // <![CDATA[ 
    if (a && b) doSomething('a & b &amp; c'); 
    // ]]> 
</script> 
<p>a & b</p> 
<p>c &amp; d</p> 

Sólo dos símbolos de unión partido - El a & b en la parte superior y el a & b en la parte inferior. Ampersands ya se escapó como &amp; y todos los signos y símbolos (escapados o no) entre <![CDATA[ y ]]> se quedan solos.

lo tanto, mi código final es ahora esto:

html.gsub(/&(?!(?:[a-zA-Z][a-zA-Z0-9]*|#\d+);)(?!(?>(?:(?!<!\[CDATA\[|\]\]>).)*)\]\]>)/m, '&amp;') 

Muchas gracias Alan. Esto es exactamente lo que necesitaba.

+0

Ach! Me sigo olvidando de Ruby usando el modificador 'm' para significar lo que todos usan 's'. Lo arreglaré. –

+0

En PHP necesita usar la opción/s (PCRE_DOTALL). La PCRE con saltos de línea o espacios no funcionó para mí, incluso cuando se usan las opciones/m (PCRE_MULTILINE) y/o/x (PCRE_EXTENDED). – feeela

Cuestiones relacionadas