20

我经常忘记正则表达式修饰符m及其s差异。有什么好的方法来记住它们?

据我了解,它们是:

'm' 用于多行,因此 将多次匹配字符串的开头和字符串的结尾^$(除以\n

's' 使得点将匹配换行符

通常,我只是使用

/some_pattern/ism

但相应地使用它们可能会更好(在我的情况下通常是“s”)。

你认为什么是记住它们的好方法,而不是每次都忘记哪个是哪个?

4

3 回答 3

23

多年来一直使用正则表达式但仍然不了解这两个修饰符如何工作的人并不罕见。正如您所观察到的,名称“多行”和“单行”并不是很有帮助。它们听起来一定是相互排斥的,但它们是完全独立的。我建议您忽略名称并专注于它们的作用:m更改锚点的行为(^$),并s更改点的行为(.)。

混合模式的一位杰出人物是 Ruby 的作者。他在 Perl 的基础上创建了自己的正则表达式实现,但他决定拥有^$始终作为行锚——也就是说,多行模式始终处于打开状态。不幸的是,他还错误地将 dot-matches-everything 模式命名为multiline。所以 Ruby 没有s修饰符,但它的m修饰符可以s做其他风格的事情。

至于总是使用/ism,我建议不要。正如您所发现的,它基本上是无害的,但它会向任何试图弄清楚正则表达式应该做什么的人(或者甚至是未来的你自己)发送一个令人困惑的信息。

于 2009-05-28T03:29:45.167 回答
9

我喜欢“man perlre”中的解释:

m 将字符串视为行。
s 将字符串视为单行

对于多行,^ 和 $ 适用于单独的行(即在换行符之前和之后)。
使用单行,^ 和 $ 适用于整体,而 \n 只是成为您可以匹配的另一个字符。

[错误]通过使用您所描述的 m 和 s,我希望第二个优先,因此您将始终使用 /ism 处于多行模式。[/错误的]

我读得不够远:
“/s”和“/m”修饰符都覆盖了 $* 设置。也就是说,无论 $* 包含什么,没有 "/m" 的 "/s" 将强制 "^" 仅匹配字符串的开头,而 "$" 仅匹配末尾(或仅在换行符之前字符串的结尾)。一起,作为 /ms,他们让“。” 匹配任何字符,同时仍然允许 "^" 和 "$" 分别匹配字符串中的换行符之后和之前。

于 2009-05-28T02:12:19.520 回答
0

2020 年更新:

我可以更清楚地写出它们是什么,以及一种记住它们的方法,并且我将其写成与 JavaScript 相关:

  1. 传统上,JS 正则表达式没有s标志。它只有m国旗。截至 2020 年 1 月,Firefox 仍然没有它,而 Chrome 有它。NodeJS 拥有它。它在 ES2018 规范中。
  2. s也称为dotallor 。singleline它实际上只是为了.匹配任何 (ASCII) 字符,包括\n, \r, \u2028(换行符), \u2029(段符)。当人们问你,什么是.匹配的?如果你回答“任何字符”,那么它并不完全正确。它是除换行符\r和 unicode 换行符和分段符之外的所有 (ASCII) 字符。为了让它真正匹配所有 ASCII 字符,它需要打开s标志。
  3. 为了克服 Firefox 或任何平台中缺少s标志,它可以是[^], [\s\S], [\d\D], etc, 或(.|\s).
  4. 就这样。这就是s传统 JavaScript 中缺少的标志。
  5. 现在是m国旗。它代表多行。它真的很简单:没有m标志,^and$将只匹配整个字符串的开头和结尾。所以"John Doe\nMary Lee".match(/^John Doe$/)不会匹配,"John Doe\nMary Lee".match(/^John Doe$/m)会匹配。就这样。不要以太复杂的方式考虑它。它只是改变了匹配方式^$匹配方式。
  6. 那么“单行”和“多行”是互斥的吗?不,他们不是。例如,如果我想匹配a任何字符,包括换行符和f, 但a必须在行首并且f必须在行尾,即使在 2000 行文本中,那么"a b c \n d e f\nha".match(/^a.*f$/ms)需要用过的。匹配,和.匹配行首和行尾。\n^$

就是这样。以上是在已经支持该s标志的 NodeJS 和 Chrome 上测试的。(并且该m标志长期以来一直受到支持)。请记住,您始终可以s使用以下方法修复标志丢失问题[^]

现在,为什么过去被ms或被ism大量使用?因为很多时候,当我们有一个非常长的字符串(例如 2000 行 HTML)时,例如我们返回的一些网页内容,我们很少希望匹配^整个字符串的开头和$结尾的整个字符串。所以这就是我们使用m标志的原因。现在,我们可能想要跨行匹配,因为(尽管不推荐使用正则表达式来匹配 HTML),例如,我们可能会使用/<h1>.*?</h1>/一个非贪婪的标题匹配。我们不介意\n内容中的 ,因为 HTML 的作者很可能有一个\n(或没有)。这就是我们使用“dotall”标志的原因s

但是,如果您尝试从网页中提取一些信息,您可能不会关心某些内容是在行首还是行尾(因为 HTML 文件中可以有空格(或作为缩进),而且它不会' 不影响页面内容(通常,除非有<pre>等),所以你不需要使用^or $,因此你可以忘记m标志。如果你不介意使用[^]*?代替.*?,那么你也可以忘记s标志——故事结束。

Perl Cookbook 用两句话说:

/m和之间的区别/s很重要:/m在换行符旁边生成^$匹配,而在换行符旁边生成/s匹配.。您甚至可以一起使用它们——它们不是相互排斥的选项。


也许这样,我永远不会忘记:

当我想跨行匹配时(通常使用 .*? 来匹配跨多行无关紧要的东西),我自然会想到多行,因此,'m'。好吧,“m”实际上不是那个,所以它是“s”。

(因为我已经很好地记住了'ism'......所以我总是能记住它不是'm',那么它一定是's')。

其他蹩脚的尝试包括:

s用于 DOTALL,用于 DOT 匹配 ALL。
m是多行的——它用于^$匹配很多次。

于 2009-05-28T02:23:38.407 回答