¿Cómo usar JavaScript regex en varias líneas?

275
var ss= "<pre>aaaa\nbbb\nccc</pre>ddd";
var arr= ss.match( /<pre.*?<\/pre>/gm );
alert(arr);     // null

Me gustaría que el bloque PRE se recupere, aunque se extienda sobre los caracteres de nueva línea. Pensé que la bandera 'm' lo hace. No.

Encontré la respuesta aquí antes de publicar. Como pensé que conocía JavaScript (leí tres libros, trabajé horas) y no había una solución existente en SO, me atreveré a publicar de todos modos. tirar piedras aquí

Entonces la solución es:

var ss= "<pre>aaaa\nbbb\nccc</pre>ddd";
var arr= ss.match( /<pre[\s\S]*?<\/pre>/gm );
alert(arr);     // <pre>...</pre> :)

¿Alguien tiene una forma menos críptica?

Editar: este es un duplicado, pero como es más difícil de encontrar que el mío, no lo elimino.

Se propone [^]como un "punto multilínea". Lo que aún no entiendo es por qué [.\n]no funciona. Supongo que esta es una de las partes tristes de JavaScript.

akauppi
fuente
29
¿Una expresión regular menos críptica? Imposible, por naturaleza.
Rubens Farias
por cierto, deberías leer: "Parsing Html: The Cthulhu Way" codinghorror.com/blog/archives/001311.html
Rubens Farias
1
El enlace cambió desde el comentario anterior: blog.codinghorror.com/parsing-html-the-cthulhu-way (5 años más tarde)
dab

Respuestas:

248

[.\n]no funciona porque .no tiene un significado especial en su interior [], solo significa un literal .. (.|\n)sería una forma de especificar "cualquier carácter, incluida una nueva línea". Si desea hacer coincidir todos los saltos de línea, lo que tendría que añadir \rademás para incluir Windows y terminaciones de línea clásica al estilo Mac OS: (.|[\r\n]).

Eso resulta ser algo engorroso, así como lento (vea la respuesta de KrisWebDev para más detalles ), por lo que un mejor enfoque sería hacer coincidir todos los caracteres de espacios en blanco y todos los caracteres que no son espacios en blanco, con [\s\S], que coincidirá con todo, y es más rápido y más simple

En general, no debe intentar utilizar una expresión regular para que coincida con las etiquetas HTML reales. Consulte, por ejemplo, estas preguntas para obtener más información sobre por qué.

En su lugar, intente buscar en el DOM la etiqueta que necesita (usar jQuery lo hace más fácil, pero siempre puede hacerlo document.getElementsByTagName("pre")con el DOM estándar), y luego busque el contenido de texto de esos resultados con una expresión regular si necesita hacer coincidir los contenidos .

Brian Campbell
fuente
Lo que estoy haciendo es hacer .wiki -> conversión HTML sobre la marcha, usando JavaScript. Por lo tanto, todavía no tengo el DOM disponible. El archivo Wiki es principalmente su propia sintaxis, pero permito que se usen etiquetas HTML si es necesario. Su consejo es muy válido, si estaba tratando en DOM con esto. Gracias. :)
akauppi
Lo suficientemente justo. Supongo que es una razón válida para querer usar expresiones regulares en HTML, aunque las sintaxis wiki mezcladas con HTML pueden tener todo tipo de divertidos casos de esquina.
Brian Campbell el
2
[\r\n]aplicado a una secuencia \ r \ n, primero coincidiría con \ r y luego \ n. Si desea hacer coincidir toda la secuencia a la vez, independientemente de si esa secuencia es \ r \ n o simplemente \ n, use el patrón.|\r?\n
Eirik Birkeland,
1
Para hacer coincidir una cadena multilínea completa , prueba el codicioso [\s\S]+.
Boaz
Solo quiero agregar para la posteridad que la sintaxis JS regex que ignora el significado del .interior []es diferente a otros marcos de expresiones regulares, particularmente el avanzado en .NET. Gente, por favor no asuman que las expresiones regulares son multiplataforma, ¡con frecuencia no lo son !
Sr. TA
330

NO lo use en (.|[\r\n])lugar de .para la coincidencia multilínea.

UTILICE en [\s\S]lugar de .para la coincidencia multilínea

Además, evite la codicia donde no sea necesario usando *?o un +?cuantificador en lugar de *o +. Esto puede tener un gran impacto en el rendimiento.

Vea el punto de referencia que he hecho: http://jsperf.com/javascript-multiline-regexp-workarounds

Using [^]: fastest
Using [\s\S]: 0.83% slower
Using (.|\r|\n): 96% slower
Using (.|[\r\n]): 96% slower

NB: También puede usar, [^]pero está en desuso en el comentario a continuación.

KrisWebDev
fuente
22
Buenos puntos, pero recomiendo no usarlos de [^]todos modos. Por un lado, JavaScript es el único sabor que conozco que respalda ese idioma, e incluso allí no se usa con tanta frecuencia como siempre [\s\S]. Por otro lado, la mayoría de los otros sabores te permiten escapar al ]enumerarlo primero. En otras palabras, en JavaScript [^][^]partidos cualquiera de los dos personajes, pero en .NET coincide con alguna de un carácter distinto ], [o ^.
Alan Moore
1
¿Cómo sabes que \Scoincidirá \ro \ncontra algún otro personaje?
Gili
3
Vea esta pregunta para obtener \ s \ S detalles. Este es un truco para hacer coincidir todos los caracteres de espacio en blanco + todos los caracteres que no son espacios en blanco = todos los caracteres. Consulte también MDN para la documentación de caracteres especiales regexp.
KrisWebDev
44
¿Alguna razón para preferir [\s\S]a otros, como [\d\D]o [\w\W]?
Phrogz
1
Permítanme señalar rápidamente que su prueba para el operador codicioso está manipulada. /<p>Can[^]*?<\/p>/no coincide con el mismo contenido que /<p>Can[^]*<\/p>/. La variante codiciosa debe cambiarse para /<p>(?:[^<]|<(?!\/p>))*<\/p>/que coincida con el mismo contenido.
3limin4t0r
19

No especifica su entorno y versión de Javascript (ECMAscript), y me doy cuenta de que esta publicación fue de 2009, pero solo para completar, con el lanzamiento de ECMA2018 ahora podemos usar la sbandera para hacer .coincidir '\ n', consulte https : //stackoverflow.com/a/36006948/141801

Así:

let s = 'I am a string\nover several\nlines.';
console.log('String: "' + s + '".');

let r = /string.*several.*lines/s; // Note 's' modifier
console.log('Match? ' + r.test(s); // 'test' returns true

Esta es una adición reciente y no funcionará en muchos entornos actuales, por ejemplo, el nodo v8.7.0 no parece reconocerlo, pero funciona en Chromium, y lo estoy usando en una prueba de tipografía que estoy escribiendo y presumiblemente se volverá más convencional a medida que pase el tiempo.

Neek
fuente
1
Esto funciona muy bien en Chrome (v67) pero rompe completamente la expresión regular (también deja de funcionar línea por línea) en IE11 e IEdge (v42)
freedomn-m
Gracias @ freedomn-m .. IE no admite una característica muy nueva es casi completamente sorprendente :) Pero sí, vale la pena mencionar dónde no funciona para salvar a cualquiera que intente "depurar" por qué su intento de usarlo no está funcionando como se esperaba.
Neek
11

[.\n]no funciona, porque el punto de entrada [](por definición regex; no solo javascript) significa el carácter de punto. Puedes usar (.|\n)(o (.|[\n\r])) en su lugar.

Y. Shoham
fuente
24
[\s\S]es el idioma de JavaScript más común para hacer coincidir todo, incluidas las nuevas líneas. Es más fácil a la vista y mucho más eficiente que un enfoque basado en la alternancia (.|\n). (Literalmente significa "cualquier personaje que sea ​​un espacio en blanco o cualquier personaje que no sea un espacio en blanco.")
Alan Moore
2
Tienes razón, pero la pregunta era sobre .y \n, y por qué [.\n]no funciona. Como se mencionó en la pregunta, [^]también es un buen enfoque.
Y. Shoham el
6

Lo he probado (Chrome) y funciona para mí (ambos [^]y [^\0]), cambiando el punto ( .) por[^\0] o [^]porque el punto no coincide con el salto de línea (Ver aquí:http://www.regular-expressions.info/dot.html ).

var ss= "<pre>aaaa\nbbb\nccc</pre>ddd";
var arr= ss.match( /<pre[^\0]*?<\/pre>/gm );
alert(arr);     //Working

Hzzkygcs
fuente
1
El problema con [^\0]es que no coincidirá con caracteres nulos a pesar de que los caracteres nulos están permitidos en las cadenas de Javascript (ver esta respuesta ).
Pato Donald
0

Además de los ejemplos mencionados anteriormente, es una alternativa.

^[\\w\\s]*$

Donde \wes para palabras y \ses para espacios en blanco

azhar22k
fuente