Comment utiliser l'expression régulière JavaScript sur plusieurs lignes?

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

Je voudrais que le bloc PRE soit récupéré, même s'il s'étend sur des caractères de nouvelle ligne. Je pensais que le drapeau «m» le faisait. Ne fait pas.

J'ai trouvé la réponse ici avant de poster. Depuis que je pensais connaître JavaScript (lire trois livres, travailler des heures) et qu'il n'y avait pas de solution existante chez SO, j'oserais poster quand même. jeter des pierres ici

La solution est donc:

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

Quelqu'un a-t-il un moyen moins cryptique?

Edit: c'est un double mais comme il est plus difficile à trouver que la mienne, je ne supprime pas.

Il propose [^]un "point multiligne". Ce que je ne comprends toujours pas, c'est pourquoi [.\n]ça ne marche pas. Je suppose que c'est l'une des tristes parties de JavaScript ..

akauppi
la source
29
Un regex moins cryptique? Impossible, par nature.
Rubens Farias
btw, vous devriez lire: "Analyse du code HTML: la manière Cthulhu" codinghorror.com/blog/archives/001311.html
Rubens Farias
1
Le lien a changé par rapport au commentaire précédent: blog.codinghorror.com/parsing-html-the-cthulhu-way (5 ans plus tard)
tamponnez le

Réponses:

248

[.\n]ne fonctionne pas car .n'a pas de signification particulière à l'intérieur de [], cela signifie simplement un littéral .. (.|\n)serait un moyen de spécifier "n'importe quel caractère, y compris une nouvelle ligne". Si vous voulez faire correspondre tous les sauts de ligne, vous devez ajouter \rainsi d'inclure Windows et classiques de fin de ligne de style Mac OS: (.|[\r\n]).

Cela s'avère être un peu lourd, mais aussi lent (voir la réponse de KrisWebDev pour plus de détails ), donc une meilleure approche serait de faire correspondre tous les caractères blancs et tous les caractères non blancs, avec [\s\S], qui correspondra à tout, et est plus rapide et plus simple.

En général, vous ne devriez pas essayer d'utiliser une expression rationnelle pour faire correspondre les balises HTML réelles. Voir, par exemple, ces questions pour plus d'informations sur pourquoi.

Au lieu de cela, essayez de rechercher dans le DOM la balise dont vous avez besoin (l'utilisation de jQuery rend cela plus facile, mais vous pouvez toujours le faire document.getElementsByTagName("pre")avec le DOM standard), puis recherchez le contenu textuel de ces résultats avec une expression régulière si vous devez faire une correspondance avec le contenu .

Brian Campbell
la source
Ce que je fais, c'est faire de la conversion .wiki -> HTML à la volée, en utilisant JavaScript. Par conséquent, je n'ai pas encore le DOM disponible. Le fichier Wiki est principalement sa propre syntaxe, mais j'autorise les balises HTML à utiliser si nécessaire. Vos conseils sont très valables, si je traitais avec DOM avec cela. Merci. :)
akauppi
C'est suffisant. Je suppose que c'est une raison valable de vouloir utiliser des expressions rationnelles sur HTML, bien que les syntaxes wiki mélangées à HTML puissent avoir toutes sortes de cas d'angle amusants eux-mêmes.
Brian Campbell
2
[\r\n]appliqué à une séquence \ r \ n, correspondrait d'abord à \ r puis à \ n. Si vous souhaitez faire correspondre la séquence entière à la fois, que cette séquence soit \ r \ n ou juste \ n, utilisez le modèle.|\r?\n
Eirik Birkeland
1
Pour faire correspondre une chaîne multiligne entière , essayez le gourmand [\s\S]+.
Boaz
Je veux juste ajouter pour la postérité que la syntaxe de regex JS ignorant la signification de .inside []est différente de celle des autres frameworks regex, en particulier celui avancé de .NET. Les gens, s'il vous plaît, ne supposez pas que les regex sont multiplates-formes, ce n'est souvent pas le cas !!
Monsieur TA
330

NE PAS utiliser (.|[\r\n])au lieu de .pour l'appariement multiligne.

A UTILISER [\s\S]au lieu de .pour l'appariement multiligne

Évitez également la gourmandise là où elle n'est pas nécessaire en utilisant *?ou +?quantifier au lieu de *ou +. Cela peut avoir un impact énorme sur les performances.

Voir le benchmark que j'ai fait: 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: Vous pouvez également utiliser [^]mais il est déconseillé dans le commentaire ci-dessous.

KrisWebDev
la source
22
Bons points, mais je déconseille de [^]toute façon l' utilisation . D'une part, JavaScript est la seule saveur que je connaisse qui prend en charge cet idiome, et même là, il est utilisé aussi loin que souvent [\s\S]. D'un autre côté, la plupart des autres saveurs vous permettent de vous échapper en les ]répertoriant d'abord. En d' autres termes, en JavaScript [^][^]correspond à tous les deux personnages, mais dans .NET il correspond à un caractère autre que ], [ou ^.
Alan Moore
1
Comment savez-vous que \Scela correspondra \rou \npar rapport à un autre personnage?
Gili
3
Voir cette question pour plus de détails sur \ s \ S. Il s'agit d'un hack pour faire correspondre tous les caractères d'espacement + tous les caractères non blancs = tous les caractères. Voir aussi MDN pour la documentation des caractères spéciaux regexp.
KrisWebDev
4
Une raison de préférer [\s\S]les autres, comme [\d\D]ou [\w\W]?
Phrogz
1
Permettez-moi de souligner rapidement que votre test pour l'opérateur gourmand est truqué. /<p>Can[^]*?<\/p>/ne correspond pas au même contenu que /<p>Can[^]*<\/p>/. La variante gourmande doit être modifiée /<p>(?:[^<]|<(?!\/p>))*<\/p>/pour correspondre au même contenu.
3limin4t0r
19

Vous ne spécifiez pas votre environnement et votre version de Javascript (ECMAscript), et je me rends compte que ce post date de 2009, mais juste pour être complet, avec la sortie d'ECMA2018, nous pouvons maintenant utiliser le sdrapeau pour faire .correspondre '\ n', voir https : //stackoverflow.com/a/36006948/141801

Donc:

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

Ceci est un ajout récent et ne fonctionnera pas dans de nombreux environnements actuels, par exemple Node v8.7.0 ne semble pas le reconnaître, mais il fonctionne dans Chromium, et je l'utilise dans un test Typescript que j'écris et probablement deviendra plus grand public au fil du temps.

Neek
la source
1
Cela fonctionne très bien dans Chrome (v67) mais casse complètement l'expression régulière (arrête également de travailler ligne par ligne) dans IE11 et IEdge (v42)
freedomn-m
Merci @ freedomn-m .. IE ne prenant pas en charge une toute nouvelle fonctionnalité n'est presque pas surprenant :) Mais oui, il convient de mentionner où cela ne fonctionne pas pour sauver quiconque essaie de «déboguer» pourquoi sa tentative d'utilisation ne fonctionne pas comme prévu.
Neek
11

[.\n]ne fonctionne pas, car dot in [](par définition regex; pas seulement javascript) signifie le caractère point. Vous pouvez utiliser (.|\n)(ou (.|[\n\r])) à la place.

Y. Shoham
la source
24
[\s\S]est l'idiome JavaScript le plus courant pour tout faire correspondre, y compris les sauts de ligne. C'est plus agréable pour les yeux et beaucoup plus efficace qu'une approche basée sur l'alternance comme (.|\n). (Cela signifie littéralement «tout personnage qui est un espace ou tout caractère qui n'est pas un espace.)
Alan Moore
2
Vous avez raison, mais la question portait sur .et \n, et pourquoi [.\n]ne fonctionne pas. Comme mentionné dans la question, [^]c'est aussi une bonne approche.
Y. Shoham
6

Je l'ai testé (Chrome) et cela fonctionne pour moi (à la fois [^]et [^\0]), en changeant le point ( .) par [^\0]ou [^], car le point ne correspond pas au saut de ligne (voir ici: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
la source
1
Le problème [^\0]est qu'il ne correspondra pas aux caractères nuls même si les caractères nuls sont autorisés dans les chaînes Javascript (voir cette réponse ).
Donald Duck
0

En plus des exemples ci-dessus, il s'agit d'une alternative.

^[\\w\\s]*$

\west pour les mots et \spour les espaces blancs

azhar22k
la source