Как использовать регулярные выражения JavaScript на нескольких строках?

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

Я бы хотел, чтобы был поднят блок PRE, даже если он охватывает символы новой строки. Я думал, что флаг «м» делает это. Не.

Нашел ответ здесь перед публикацией. Так как я думал, что знаю JavaScript (прочитал три книги, работал часами), и в SO не было никакого решения, я все же осмелюсь опубликовать. бросать камни сюда

Итак, решение таково:

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

У кого-нибудь есть менее загадочный способ?

Изменить: это дубликат, но так как его сложнее найти, чем я, я не удаляю.

Это предлагается [^]как "многострочная точка". Что я до сих пор не понимаю, так это почему [.\n]не работает. Думаю, это одна из печальных частей JavaScript.

akauppi
источник
29
Менее загадочное регулярное выражение? Невозможно по природе.
Рубенс Фариас
Кстати, вы должны прочитать: «Разбор HTML: Путь Ктулху» codinghorror.com/blog/archives/001311.html
Фариас Рубенса
1
Ссылка изменилась по сравнению с предыдущим комментарием: blog.codinghorror.com/parsing-html-the-cthulhu-way (5yrs-иш позже)
мазок

Ответы:

248

[.\n]не работает, потому что .не имеет особого значения внутри [], это просто означает буквальный .. (.|\n)будет способ указать «любой символ, включая перевод строки». Если вы хотите , чтобы соответствовать всем новым строкам, вам нужно будет добавить , \rа также включить Окна и классического Mac OS стиль завершение строк: (.|[\r\n]).

Это оказывается несколько громоздким, а также медленным (см. Подробности в ответе KrisWebDev ), поэтому лучшим подходом было бы сопоставить все пробельные символы и все непробельные символы с [\s\S], что будет соответствовать всему, быстрее и проще.

В общем, вы не должны пытаться использовать регулярное выражение для соответствия фактическим тегам HTML. См., Например, эти вопросы для получения дополнительной информации о том, почему.

Вместо этого попробуйте на самом деле найти в DOM нужный вам тег (использование jQuery делает это проще, но вы всегда можете сделать это document.getElementsByTagName("pre")со стандартным DOM), а затем выполнить поиск текстового содержимого этих результатов с помощью регулярного выражения, если вам нужно сопоставить его с содержимым. ,

Брайан Кэмпбелл
источник
Я делаю .wiki -> конвертацию HTML на лету, используя JavaScript. Поэтому у меня пока нет доступной DOM. Файл Wiki в основном имеет собственный синтаксис, но я разрешаю использовать теги HTML, если это необходимо. Ваш совет очень действителен, если бы я имел дело с DOM с этим. Спасибо. :)
akauppi
Справедливо. Я полагаю, что это является веской причиной для того, чтобы использовать регулярные выражения в HTML, хотя синтаксисы вики, смешанные с HTML, могут иметь все виды забавных угловых случаев сами.
Брайан Кэмпбелл
2
[\r\n]применяется к последовательности \ r \ n, сначала будет соответствовать \ r, а затем \ n. Если вы хотите сопоставить всю последовательность сразу, независимо от того, является ли эта последовательность \ r \ n или просто \ n, используйте шаблон.|\r?\n
Eirik Birkeland
1
Чтобы сопоставить всю многострочную строку, попробуйте жадный [\s\S]+.
Вооз
Я просто хочу добавить для потомков, что синтаксис регулярных выражений JS, игнорирующий значение .внутри [], отличается от других сред регулярных выражений, в частности от продвинутого в .NET. Люди, пожалуйста, не думайте, что регулярные выражения являются кросс-платформенными, а зачастую и нет !!
г-н ТА
330

НЕ использовать (.|[\r\n])вместо .многострочного сопоставления.

DO использовать [\s\S]вместо .для многострочного согласования

Кроме того, избегайте жадности там, где это не нужно, используя *?или +?квантификатор вместо *или +. Это может оказать огромное влияние на производительность.

Посмотрите тест, который я сделал: 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: Вы также можете использовать, [^]но это не рекомендуется в комментариях ниже.

KrisWebDev
источник
22
Хорошие моменты, но я рекомендую против использования в [^]любом случае. С одной стороны, JavaScript - единственный известный мне вариант, который поддерживает эту идиому, и даже там он используется далеко не так часто, как [\s\S]. С другой стороны, большинство других вкусов позволяют вам избежать ], перечислив его первым. Другими словами, в JavaScript [^][^]соответствует любым двум символам, но в .NET он соответствует любому один символ , отличный ], [или ^.
Алан Мур
1
Как вы знаете, что \Sбудет соответствовать \rили \nпротив какого-либо другого персонажа?
Гили
3
Смотрите этот вопрос для деталей \ s \ S. Это хак для соответствия всем символам пробела + всем непробельным символам = всем символам. Смотрите также MDN для документации специальных символов регулярных выражений.
KrisWebDev
4
Любая причина, чтобы предпочесть [\s\S]другим, как [\d\D]или [\w\W]?
Phrogz
1
Позвольте мне быстро указать, что ваш тест для жадного оператора сфальсифицирован. /<p>Can[^]*?<\/p>/не соответствует тому же контенту, что и /<p>Can[^]*<\/p>/. Жадный вариант должен быть изменен, /<p>(?:[^<]|<(?!\/p>))*<\/p>/чтобы соответствовать тому же содержанию.
3limin4t0r
19

Вы не указываете свою среду и версию Javascript (ECMAscript), и я понимаю, что этот пост был за 2009 год, но только для полноты, с выпуском ECMA2018 теперь мы можем использовать sфлаг, чтобы вызвать .совпадение с \ n, см. Https : //stackoverflow.com/a/36006948/141801

Таким образом:

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

Это недавнее добавление, которое не будет работать во многих современных средах, например, Node v8.7.0, похоже, не распознает его, но работает в Chromium, и я использую его в тесте Typescript, который я пишу, и предположительно это со временем станет более популярным.

NEEK
источник
1
Это прекрасно работает в Chrome (v67), но полностью нарушает регулярное выражение (также перестает работать построчно) в IE11 и IEdge (v42)
freedomn-m
Спасибо @ freedomn-m .. IE, не поддерживающий очень новую функцию, почти не удивляет :) Но да, стоит упомянуть, где это не работает, чтобы спасти кого-либо, пытающегося «отладить», почему их попытка использовать его не работает как и ожидалось.
Neek
11

[.\n]не работает, потому что точка в [](по определению регулярного выражения; не только в javascript) означает символ точки. Вы можете использовать (.|\n)(или (.|[\n\r])) вместо этого.

Й. Шохам
источник
24
[\s\S]является наиболее распространенной идиомой JavaScript для сопоставления всего, включая переводы строк. Это проще для глаз и намного эффективнее, чем подход, основанный на чередовании (.|\n). (Это буквально означает «любой символ, который является пробелом или любой символ, который не является пробелом.)
Алан Мур
2
Вы правы, но вопрос был о .и \n, и почему [.\n]не работает. Как уже упоминалось в вопросе, [^]это также хороший подход.
Й. Шохам
6

Я проверил его (Chrome), и он работает для меня (как [^]и [^\0]), изменяя точку ( .) либо на, [^\0]либо [^], потому что точка не соответствует разрыву строки (см. Здесь: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
источник
1
Проблема в [^\0]том, что он не будет соответствовать нулевым символам, даже если в строках Javascript разрешены нулевые символы (см. Этот ответ ).
Дональд Дак
0

Помимо вышеприведенных примеров, это альтернатива.

^[\\w\\s]*$

Где \wдля слов и \sдля пробелов

azhar22k
источник