Wie verwende ich JavaScript-Regex über mehrere Zeilen?

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

Ich möchte, dass der PRE-Block aufgenommen wird, obwohl er sich über Zeilenumbrüche erstreckt. Ich dachte, die 'm'-Flagge macht es. Nicht.

Hier vor dem Posten die Antwort gefunden . Da ich dachte, ich kenne JavaScript (drei Bücher lesen, Arbeitsstunden) und es bei SO keine Lösung gab, werde ich es trotzdem wagen, etwas zu posten. wirf hier Steine

Die Lösung lautet also:

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

Hat jemand einen weniger kryptischen Weg?

Bearbeiten: Dies ist ein Duplikat, aber da es schwieriger zu finden ist als meins, entferne ich es nicht.

Es wird [^]als "mehrzeiliger Punkt" vorgeschlagen. Was ich immer noch nicht verstehe ist, warum [.\n]es nicht funktioniert. Ich denke, dies ist einer der traurigen Teile von JavaScript.

Akauppi
quelle
29
Eine weniger kryptische Regex? Von Natur aus unmöglich.
Rubens Farias
Übrigens sollten Sie lesen: "Parsing Html: The Cthulhu Way" codinghorror.com/blog/archives/001311.html
Rubens Farias
1
Der Link hat sich gegenüber dem vorherigen Kommentar geändert: blog.codinghorror.com/parsing-html-the-cthulhu-way (5 Jahre später)
tupfen

Antworten:

247

[.\n]funktioniert nicht, weil .es keine besondere Bedeutung hat [], es bedeutet nur ein Literal .. (.|\n)wäre eine Möglichkeit, "ein beliebiges Zeichen, einschließlich einer neuen Zeile" anzugeben. Wenn Sie alle Zeilenumbrüche abgleichen möchten, müssen Sie auch Zeilenenden \rfür Windows und klassische Mac OS-Zeilen hinzufügen : (.|[\r\n]).

Das stellt sich als etwas umständlich und langsam heraus (siehe KrisWebDevs Antwort für Details ). Ein besserer Ansatz wäre es daher, alle Leerzeichen und alle Nicht-Leerzeichen mit zu vergleichen [\s\S], die zu allem passen und schneller und schneller sind einfacher.

Im Allgemeinen sollten Sie nicht versuchen, einen regulären Ausdruck zu verwenden, der mit den tatsächlichen HTML-Tags übereinstimmt. Weitere Informationen zum Warum finden Sie beispielsweise in diesen Fragen .

Versuchen Sie stattdessen, das DOM tatsächlich nach dem gewünschten Tag zu durchsuchen (die Verwendung von jQuery erleichtert dies, aber Sie können dies immer document.getElementsByTagName("pre")mit dem Standard-DOM tun ), und durchsuchen Sie dann den Textinhalt dieser Ergebnisse mit einem regulären Ausdruck, wenn Sie mit dem Inhalt übereinstimmen müssen .

Brian Campbell
quelle
Ich mache .wiki -> HTML-Konvertierung im laufenden Betrieb mit JavaScript. Daher habe ich das DOM noch nicht zur Verfügung. Die Wiki-Datei ist meistens eine eigene Syntax, aber ich erlaube, dass HTML-Tags bei Bedarf verwendet werden. Ihr Rat ist sehr gültig, wenn ich mich damit in DOM befasst habe. Vielen Dank. :)
Akauppi
Meinetwegen. Ich nehme an, das ist ein triftiger Grund, reguläre Ausdrücke für HTML verwenden zu wollen, obwohl mit HTML gemischte Wiki-Syntaxen selbst alle möglichen lustigen Eckfälle haben können.
Brian Campbell
2
[\r\n]angewendet auf eine Sequenz \ r \ n, würde zuerst \ r und dann \ n übereinstimmen. Wenn Sie die gesamte Sequenz auf einmal abgleichen möchten, unabhängig davon, ob diese Sequenz \ r \ n oder nur \ n ist, verwenden Sie das Muster.|\r?\n
Eirik Birkeland
1
Versuchen Sie es mit der Gier, um eine ganze mehrzeilige Zeichenfolge abzugleichen [\s\S]+.
Boaz
Ich möchte nur für die Nachwelt hinzufügen , dass JS Regex Syntax , um die Bedeutung des Ignorierens .innen []ist anders als andere Regex - Frameworks, insbesondere die fortschrittlichste in .NET. Leute, bitte geht nicht davon aus, dass Regexe plattformübergreifend sind, das sind sie häufig nicht !!
Herr TA
330

NICHT (.|[\r\n])anstelle von .mehrzeiligem Matching verwenden.

Verwenden Sie [\s\S]anstelle von .für mehrzeiligen Abgleich

Vermeiden Sie auch greediness wo nicht benötigt durch die Verwendung *?oder +?quantifier statt *oder +. Dies kann enorme Auswirkungen auf die Leistung haben.

Siehe den von mir erstellten Benchmark: 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: Sie können auch verwenden, [^]aber es ist im folgenden Kommentar veraltet.

KrisWebDev
quelle
22
Gute Punkte, aber ich empfehle [^]trotzdem nicht zu verwenden. Einerseits ist JavaScript die einzige mir bekannte Variante, die diese Redewendung unterstützt, und selbst dort wird sie bei weitem nicht so oft verwendet wie [\s\S]. Auf der anderen Seite können Sie mit den meisten anderen Geschmacksrichtungen dem entkommen, ]indem Sie es zuerst auflisten. Mit anderen Worten, in JavaScript [^][^]paßt alle zwei Charaktere, aber in .NET Entsprechung für jeden ein anderes Zeichen als ], [oder ^.
Alan Moore
1
Woher weißt du, dass \Sdas zu \roder \ngegen einen anderen Charakter passt?
Gili
3
Siehe diese Frage für \ s \ S Details. Dies ist ein Hack, der allen Leerzeichen + allen Nicht-Leerzeichen = allen Zeichen entspricht. Siehe auch MDN für die Dokumentation zu Regexp-Sonderzeichen.
KrisWebDev
4
Gibt es einen Grund, [\s\S]anderen den Vorzug zu geben , wie [\d\D]oder [\w\W]?
Phrogz
1
Lassen Sie mich schnell darauf hinweisen, dass Ihr Test für den gierigen Bediener manipuliert ist. /<p>Can[^]*?<\/p>/stimmt nicht mit dem gleichen Inhalt überein wie /<p>Can[^]*<\/p>/. Die gierige Variante sollte so geändert werden, dass /<p>(?:[^<]|<(?!\/p>))*<\/p>/sie dem gleichen Inhalt entspricht.
3limin4t0r
19

Sie geben Ihre Umgebung und Version von Javascript (ECMAscript) nicht an, und mir ist klar, dass dieser Beitrag aus dem Jahr 2009 stammt. Der Vollständigkeit halber können wir mit der Veröffentlichung von ECMA2018 jetzt das sFlag verwenden, um .die Übereinstimmung mit '\ n' zu bewirken , siehe https : //stackoverflow.com/a/36006948/141801

So:

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

Dies ist eine neue Erweiterung, die in vielen aktuellen Umgebungen nicht funktioniert. Beispielsweise scheint Node v8.7.0 sie nicht zu erkennen, funktioniert jedoch in Chromium und ich verwende sie in einem Typescript-Test, den ich schreibe, und vermutlich auch wird im Laufe der Zeit mehr Mainstream werden.

Neek
quelle
1
Dies funktioniert hervorragend in Chrome (v67), unterbricht jedoch den regulären Ausdruck (funktioniert auch nicht mehr zeilenweise) in IE11 und IEdge (v42)
Freedomn-M
Thanks @ libernn-m .. IE unterstützt keine sehr neue Funktion ist fast nicht überraschend :) Aber ja, es ist erwähnenswert, wo es nicht funktioniert, um jemanden zu retten, der versucht zu "debuggen", warum sein Versuch, es zu verwenden, nicht funktioniert wie erwartet.
Neek
11

[.\n]funktioniert nicht, weil dot in [](per Regex-Definition; nicht nur Javascript) das Punktzeichen bedeutet. Sie können stattdessen (.|\n)(oder (.|[\n\r])) verwenden.

Y. Shoham
quelle
24
[\s\S]ist die gebräuchlichste JavaScript-Redewendung, um alles abzugleichen, einschließlich Zeilenumbrüche. Es ist augenschonender und viel effizienter als ein alternationsbasierter Ansatz wie (.|\n). (Es bedeutet wörtlich "jedes Zeichen, das Leerzeichen ist, oder jedes Zeichen, das kein Leerzeichen ist.)
Alan Moore
2
Sie haben Recht, aber die Frage war über .und \n, und warum [.\n]funktioniert nicht. Wie in der Frage erwähnt, [^]ist das auch ein netter Ansatz.
Y.
6

Ich habe es getestet (Chrome) und es funktioniert für mich (beide [^]und [^\0]), indem ich den Punkt ( .) entweder [^\0]oder [^]geändert habe, weil der Punkt nicht mit dem Zeilenumbruch übereinstimmt (siehe hier: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
quelle
1
Das Problem dabei [^\0]ist, dass es nicht mit Nullzeichen übereinstimmt, obwohl Nullzeichen in Javascript-Zeichenfolgen zulässig sind (siehe diese Antwort ).
Donald Duck
0

Zusätzlich zu den oben genannten Beispielen ist es eine Alternative.

^[\\w\\s]*$

Wo \wist für Wörter und \sist für Leerzeichen

azhar22k
quelle