Jak korzystać z wyrażenia regularnego JavaScript w wielu wierszach?

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

Chciałbym, aby blok PRE został pobrany, nawet jeśli obejmuje on znaki nowego wiersza. Myślałem, że flaga „m” to robi. Nie.

Znalazłem odpowiedź tutaj przed opublikowaniem. Kiedy pomyślałem, że znam JavaScript (przeczytałem trzy książki, przepracowałem godziny) i nie było w SO żadnego rozwiązania, odważę się napisać. rzucaj kamieniami tutaj

Tak więc rozwiązaniem jest:

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

Czy ktoś ma mniej tajemniczy sposób?

Edycja: to jest duplikatem, ale ponieważ jest to trudniejsze do znalezienia niż moje, ja nie usuwać.

Proponuje się [^]jako „kropkę wielowierszową”. Nadal nie rozumiem, dlaczego [.\n]nie działa. Domyślam się, że jest to jedna ze smutnych części JavaScript.

akauppi
źródło
29
Mniej tajemnicze wyrażenie regularne? Niemożliwe z natury.
Rubens Farias
btw, powinieneś przeczytać: „Parsing Html: The Cthulhu Way” codinghorror.com/blog/archives/001311.html
Rubens Farias
1
Łącze zmieniło od poprzedniego komentarza: blog.codinghorror.com/parsing-html-the-cthulhu-way (5yrs-owski później)
dab

Odpowiedzi:

248

[.\n]nie działa, ponieważ .nie ma żadnego specjalnego znaczenia [], oznacza po prostu dosłowność .. (.|\n)byłby sposób na określenie „dowolnego znaku, w tym nowego wiersza”. Jeśli chcesz dopasować wszystkie znaki nowej linii, musisz również dodać, \raby uwzględnić zakończenia linii w stylu Windows i Mac OS:(.|[\r\n]) .

To okazuje się nieco kłopotliwe, a także powolne (szczegóły w odpowiedzi KrisWebDev ), więc lepszym rozwiązaniem byłoby dopasowanie wszystkich znaków spacji i wszystkich znaków spacji[\s\S] którymi, które będą pasować do wszystkiego, i jest szybszy i prostsze.

Zasadniczo nie należy próbować używać wyrażenia regularnego w celu dopasowania do rzeczywistych tagów HTML. Zobacz na przykład te pytania aby uzyskać więcej informacji o tym, dlaczego.

Zamiast tego spróbuj przeszukać DOM w poszukiwaniu potrzebnego znacznika (użycie jQuery ułatwia to, ale zawsze możesz to zrobić document.getElementsByTagName("pre")ze standardowym DOM), a następnie przeszukaj zawartość tekstową wyników za pomocą wyrażenia regularnego, jeśli chcesz dopasować do zawartości .

Brian Campbell
źródło
To, co robię, to konwersja .wiki -> HTML w locie, przy użyciu JavaScript. Dlatego nie mam jeszcze dostępnego modelu DOM. Plik Wiki ma swoją własną składnię, ale w razie potrzeby zezwalam na używanie tagów HTML. Twoja rada jest bardzo ważna, gdybym miał z tym do czynienia w DOM. Dzięki. :)
akauppi
Słusznie. Podejrzewam, że jest to uzasadniony powód, aby chcieć używać wyrażeń regularnych w HTML, chociaż składnie wiki mieszane z HTML mogą zawierać same różnego rodzaju zabawne przypadki narożne.
Brian Campbell
2
[\r\n]zastosowane do sekwencji \ r \ n, najpierw pasowałoby \ r, a następnie \ n. Jeśli chcesz dopasować całą sekwencję naraz, niezależnie od tego, czy jest to sekwencja \ r \ n czy tylko \ n, użyj wzoru.|\r?\n
Eirik Birkeland
1
Aby dopasować cały ciąg multilinii, spróbuj chciwości [\s\S]+.
Boaz
Chciałbym tylko dodać dla potomności, że składnia wyrażeń regularnych JS ignorująca znaczenie .wewnątrz []jest inna niż inne frameworki wyrażeń regularnych, szczególnie zaawansowana w .NET. Ludzie, proszę nie zakładać, że wyrażenia regularne są wieloplatformowe, często nie są !!
Pan TA
330

NIE używaj (.|[\r\n])zamiast .dopasowywania wielowierszowego.

UŻYWAJ [\s\S]zamiast .dopasowywania wielowierszowego

Unikaj także zachłanności, gdy nie jest to konieczne, używając *?lub +?kwantyfikatora zamiast *lub +. Może to mieć ogromny wpływ na wydajność.

Zobacz test, który wykonałem: 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

Uwaga: Możesz także użyć, [^]ale jest to przestarzałe w poniższym komentarzu.

KrisWebDev
źródło
22
Dobre punkty, ale i tak nie polecam [^]. Z jednej strony JavaScript jest jedynym znanym smakiem, który obsługuje ten idiom, a nawet tam jest używany tak często jak [\s\S]. Z drugiej strony, większość innych smaków pozwala ci uciec przed ]listą. Innymi słowy, w JavaScript [^][^]pasują dowolne dwa znaki, ale w .NET pasuje dowolny jeden znak inny niż ], [lub ^.
Alan Moore,
1
Skąd wiesz, że \Sbędzie pasować \ralbo \nkontra innej postaci?
Gili
3
Zobacz to pytanie, aby uzyskać szczegółowe informacje. To jest hack, aby dopasować wszystkie znaki spacji + wszystkie znaki niebiałe = wszystkie znaki. Zobacz także MDN, aby uzyskać dokumentację wyrażeń regularnych wyrażeń regularnych.
KrisWebDev,
4
Czy jest jakiś powód, aby wybierać [\s\S]spośród innych, takich jak [\d\D]lub [\w\W]?
Phrogz
1
Chciałbym szybko zauważyć, że twój test na chciwego operatora jest sfałszowany. /<p>Can[^]*?<\/p>/nie pasuje do tej samej treści co /<p>Can[^]*<\/p>/. Chciwy wariant należy zmienić, /<p>(?:[^<]|<(?!\/p>))*<\/p>/aby pasował do tej samej treści.
3limin4t0r
19

Nie określasz swojego środowiska i wersji Javascript (ECMAscript) i zdaję sobie sprawę, że ten post pochodzi z 2009 roku, ale dla kompletności, dzięki wydaniu ECMA2018 możemy teraz użyć sflagi, .aby dopasować „\ n”, patrz https : //stackoverflow.com/a/36006948/141801

A zatem:

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

Jest to najnowszy dodatek i nie będzie działać w wielu obecnych środowiskach, na przykład wydaje się, że Node v7.7.0 go nie rozpoznaje, ale działa w Chromium i używam go w teście maszynopisu, który piszę i prawdopodobnie z czasem stanie się coraz bardziej popularny.

Neek
źródło
1
Działa to świetnie w Chrome (v67), ale całkowicie łamie regex (przestaje działać linia po linii) w IE11 i IEdge (v42)
freedomn-m
Dzięki @ freedomn-m .. Nieobsługiwanie nowej funkcji przez IE jest prawie zupełnie zaskakujące :) Ale tak, warto wspomnieć, gdzie to nie działa, aby uratować każdego, kto próbuje „debugować”, dlaczego ich próba użycia go nie działa zgodnie z oczekiwaniami.
Neek
11

[.\n]nie działa, ponieważ kropka [](z definicji wyrażenia regularnego; nie tylko javascript) oznacza znak kropki. Zamiast tego możesz użyć (.|\n)(lub (.|[\n\r])).

Y. Shoham
źródło
24
[\s\S]jest najczęstszym idiomem JavaScript do dopasowania wszystkiego, w tym nowych linii. To jest łatwiejsze dla oczu i znacznie bardziej wydajne niż podejście oparte na naprzemienności(.|\n) . (To dosłownie oznacza „każdą postać, która jest spacją lub każdą postacią, która nie jest spacją.”
Alan Moore,
2
Masz rację, ale pytanie dotyczyło . , a \n, i dlaczego [.\n]nie działa. Jak wspomniano w pytaniu, [^]jest to również miłe podejście.
Y. Shoham
6

Przetestowałem to (Chrome) i działa dla mnie (zarówno [^]i[^\0] ), zmieniając kropkę ( .) o jeden [^\0]lub [^], ponieważ kropka nie pasuje do podziału linii (patrz tutaj: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
źródło
1
Problem [^\0]polega na tym, że nie będzie pasował do znaków zerowych, mimo że w ciągach JavaScript dozwolone są znaki puste (patrz ta odpowiedź ).
Kaczor Donald
0

Oprócz wyżej wymienionych przykładów jest to alternatywa.

^[\\w\\s]*$

Gdzie \wjest dla słów i \sbiałych znaków

azhar22k
źródło