如何在多行中使用JavaScript正则表达式?

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

我想让PRE块被选中,即使它跨越了换行符。我以为'm'标志可以实现这一点,但事实并非如此。

在发布这篇文章之前,我在这里找到了答案。尽管我读了三本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的悲哀之一..


46
更易懂的正则表达式?不可能,因为它的本质就是晦涩难懂。 - Rubens Farias
顺便提一下,你应该阅读一下《解析Html:克苏鲁的方式》http://www.codinghorror.com/blog/archives/001311.html - Rubens Farias
2
链接已从之前的评论更改:http://blog.codinghorror.com/parsing-html-the-cthulhu-way/(大约5年后) - dab
8个回答

394

不要使用(.|[\r\n])进行多行匹配,而是应该使用[\s\S]

此外,在不需要的情况下避免贪婪匹配,可以使用*?+?量词代替*+。这会对性能产生巨大影响。

请参阅我所做的基准测试:https://jsben.ch/R4Hxu

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

注意: 您也可以使用[^],但它已经在下面的注释中被弃用了。


29
这些观点不错,但我仍然建议不要使用 [^]。一方面,JavaScript 是我所知道的唯一支持此习惯用法的语言,即使在 JavaScript 中,它的使用频率也远不及 [\s\S]。另一方面,大多数其他语言都允许通过将 ] 放在字符组的首位来转义它。换句话说,在 JavaScript 中,[^][^] 匹配任意两个字符,但在 .NET 中,它匹配除了][^ 之外的任何一个字符。 - Alan Moore
3
дҪ еҰӮдҪ•зҹҘйҒ“\SдјҡдёҺ\rжҲ–\nеҢ№й…ҚиҖҢдёҚжҳҜе…¶д»–еӯ—з¬Ұпјҹ - Gili
9
参见此问题以获取\s\S的详细信息。这是一种匹配所有空白字符+所有非空白字符=所有字符的技巧。有关正则表达式特殊字符的文档,请参见MDN - KrisWebDev
9
相对于其他类似的表达式(如[\d\D][\w\W]),有什么理由更喜欢使用[\s\S] - Phrogz
1
让我快速指出,你对贪婪操作符的测试是有问题的。/<p>Can[^]*?<\/p>//<p>Can[^]*<\/p>/ 不匹配相同的内容。贪婪变体应该改为 /<p>(?:[^<]|<(?!\/p>))*<\/p>/ 来匹配相同的内容。 - 3limin4t0r
显示剩余2条评论

283

[.\n]无法匹配成功,因为.[]内没有特殊含义,它只是一个字面的.。使用(.|\n)可以匹配任何字符,包括换行符。如果要匹配所有换行符,还需要添加\r以包括Windows和经典Mac OS样式的行结束符:(.|[\r\n])

然而,这种方法有点麻烦,而且速度较慢(详见KrisWebDev的回答),更好的方法是使用[\s\S]来匹配所有空格和非空格字符,这将匹配所有内容,并且更快更简单。

通常情况下,不应尝试使用正则表达式来匹配实际的HTML标记。详见这些 问题以获取更多相关信息。

相反,应该尝试在DOM中查找所需的标记(使用jQuery可以更轻松地实现,但您始终可以使用标准DOM的document.getElementsByTagName("pre")),然后在这些结果的文本内容中搜索需要匹配的内容。


我正在使用JavaScript实时进行.wiki -> HTML转换。因此,我还没有可用的DOM。Wiki文件大多是其自身的语法,但如果需要,我允许使用HTML标记。如果我在处理DOM,则您的建议非常有效。谢谢。 :) - akauppi
好的,我想这是想要在HTML上使用正则表达式的有效理由,尽管混合使用维基语法和HTML可能会出现各种有趣的边角情况。 - Brian Campbell
3
应用于序列 [\r\n] 的模式会首先匹配 \r 然后是 \n。如果你想一次匹配整个序列,无论该序列是\r\n还是只有\n,请使用模式.|\r?\n - Eirik Birkeland
3
尝试匹配整个多行字符串时,可以使用贪婪的 [\s\S]+ - Boaz
我只是想为后人补充一点,JS正则表达式语法忽略[]内部的.的含义与其他正则表达式框架(尤其是.NET中的高级框架)是不同的。请大家不要假设正则表达式是跨平台的,它们经常是不同的! - Mr. TA
另外,转义时要小心:在 new RegExp('...') 中,您需要 [\\s\\S],而在 /.../ 中,一个简单的 [\s\S] 就足够了!还有一件事要注意,就是不要使用多行标志! - bersling

56

你没有说明你的环境和 JavaScript(ECMAScript) 的版本,我意识到这篇文章是从2009年发布的,但为了完整起见:

随着 ECMA2018 的发布,我们现在可以使用 s 标志来导致 . 匹配 \n(参见https://dev59.com/uWAg5IYBdhLWcg3wjrgZ#36006948)。

因此:

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测试,随着时间的推移,它可能会变得更加流行。


3
这在Chrome(v67)中运作得很好,但在IE11和IEdge(v42)中完全破坏了正则表达式(同时停止逐行工作)。 - freedomn-m
感谢@freedomn-m.. IE不支持一个非常新的功能几乎完全是不足为奇的 :) 但是,值得一提的是,在它不起作用的地方提醒一下,以免有人试图“调试”为什么他们尝试使用它时无法按预期工作。 - Neek

21

现在有s(单行)修饰符,它可以让点号匹配换行符:)\s也将匹配换行符:D

只需要在斜线后面添加s即可

 /<pre>.*?<\/pre>/gms

这对我很有用! - lmiller1990

13

[.\n]并不起作用,因为在[]中,点号代表字符“.”。您可以使用(.|\n)(或者(.|[\n\r]))代替。


28
[\s\S]是匹配包括换行符在内的任何字符最常见的JavaScript用法。它不仅更易于阅读,而且比基于替换的方法(如 (.|\n) )更加高效。(它字面上的意思是“任何空格字符或任何非空格字符”)。 - Alan Moore
2
你说得没错,但问题是关于 .\n 以及为什么 [.\n] 不起作用。正如问题中提到的,[^] 也是一个不错的方法。 - Yaakov Shoham

11

我已经测试过了(使用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


1
[^\0] 的问题在于它无法匹配空字符,尽管在 Javascript 字符串中允许使用空字符(请参见此答案)。 - Donald Duck

0
除了上述的例子,这是一个替代方案。
^[\\w\\s]*$

\w 用于匹配单词字符,\s 用于匹配空白字符


0

[\\w\\s]*

这个对我非常有帮助,特别是用于匹配包含换行符的多个内容,其他所有答案最终都只将所有匹配项分组在一起。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,