如何。{n}和?<option>:适用于Ruby 1.9.3环境下的`Regexp` </option>

时间:2013-01-17 18:31:38

标签: ruby regex ruby-1.9.3

我试图了解{1.9}环境中.{n}?<option>:Regexp中的工作原理。但无法理解下面的代码如何产生输出:

irb(main):001:0> %W{fin\n fi\n\n \n\n fin\r\n find}.grep /f.{2}(?m:.)\Z/
=> ["fin\n", "fin\r\n", "find"]
irb(main):002:0> %W{fin\n fi\n\n \n\n fin\r\n find}.grep /f.{1}(?m:.)\Z/
=> ["fin\n", "fi\n\n"]
irb(main):003:0> %W{fin\n fi\n\n \n\n fin\r\n find}.grep /f.{1}(?m:.)\Z/
=> []
irb(main):010:0> %W{fin\n fi\n\n \n\n fin\r\n find}.grep /f.(?m:.)\Z/
=> ["fin\n", "fi\n\n"]
irb(main):011:0> %W{fin\n fi\n\n \n\n fin\r\n find}.grep /f.(m:.)\Z/
=> []
irb(main):012:0> %W{fin\n fi\n\n \n\n fin\r\n find}.grep /f.(?m:.)\z/
=> []

任何人都可以帮助我理解上述代码如何在IRB终端中生成上述输出?

谢谢,


根据@Kevin最后一段 ,我尝试了以下内容,找到了预期和理想的输出:

irb(main):014:0> %W{fin fi\n\n \n\n fin\r\n find}.grep /f.(?m:.)\z/
=> ["fin"]
irb(main):015:0> %W{fin fi\n\n \n\n fin\r find}.grep /f.(?m:.)\z/
=> ["fin"]
irb(main):016:0> %W{fin fi\n \n\n fin\r\n find}.grep /f.(?m:.)\z/
=> ["fin", "fi\n"]
irb(main):017:0> %W{fin fi\n \n\n fr\n find}.grep /f.(?m:.)\z/
=> ["fin", "fi\n", "fr\n"]
irb(main):018:0>

非常感谢 @Kevin 。你帮我理解了整个概念!

1 个答案:

答案 0 :(得分:3)

{n}表示“重复前一个原子n次”。在正则表达式中,原子是一个独立的单元。所以单个字符就是一个原子。这是一个点。一个组也是一个原子(包含其他原子),也就是一个字符类。因此.{n}表示“匹配n字符”(因为.表示“匹配任何字符”)。

请注意,{n}不像是反向引用,因为它不必在每次重复时匹配相同的文本。 .{5}的行为与.....完全相同。

这种结构也更强大。它可以采用两个数字,并且它匹配整个范围的重复计数。所以.{3,5}表示“匹配3到5个字符”。 .{3,}表示“匹配3个或更多字符”。如果您愿意,?可以替换为{0,1}*替换为{0,}+替换为{1,}


?<option:实际上不是一件事。它是(?<option>:<pattern>),这会在<option>期间打开<pattern>中列出的所有标记。它就像一个组,除了它实际上没有创建一个后引用。因此,表达式(?m:.)表示“匹配一个字符,好像标记m已打开”。鉴于m的行为与nhahtdh在评论中所说的“匹配\ n”,表达式.(?m:.).表示“匹配除换行符之外的任何字符,后跟任何字符,后跟除换行符之外的任何字符”。

这种结构有两个好处。首先,它允许您只有一个标志适用于模式的一部分,这可能偶尔有用。其次,如果将整个模式包装在此构造中,则无论表达式在何处使用,您都可以控制应用于正则表达式的标志。当您以用户身份提供正则表达式并且无法控制程序源时,这非常有用。


让我们来看看你给出的例子:

> %W{fin\n fi\n\n \n\n fin\r\n find}.grep /f.{2}(?m:.)\Z/
=> ["fin\n", "fin\r\n", "find"]

您的模式/f.{2}(?m:.)\Z/表示“匹配f,后跟任意两个字符(但换行符),后跟任何字符,并锚定到字符串的末尾或刚好在换行符之前”。

因此,在3场比赛的每场比赛中,fin都与f.{2}相匹配。 (?m:.)匹配第一个\n,第二个\r和第三个d。并且\Z匹配第一个字符串的结尾,就在第二个字符串中的换行符之前,以及第三个字符串中字符串的结尾。

fi\n\n不匹配,因为\n来自..{2}没有m标记,因此无法与此匹配。

> %W{fin\n fi\n\n \n\n fin\r\n find}.grep /f.{1}(?m:.)\Z/
=> ["fin\n", "fi\n\n"]

此处fi在两种情况下均匹配f.{1}。在这两种情况下,(?m:.)匹配n\n,并且\Z匹配换行符。

fin\r\n不匹配,因为\Z只会在字符串中的最后一行之前匹配,而不会在CRLF对之前匹配。并且find不匹配,因为没有任何内容可以匹配d

> %W{fin\n fi\n\n \n\n fin\r\n find}.grep /f.{1}(?m:.)\Z/
=> []

我认为你有副本&amp;在这里粘贴错误。这与之前的模式和匹配相同。

> %W{fin\n fi\n\n \n\n fin\r\n find}.grep /f.(?m:.)\Z/
=> ["fin\n", "fi\n\n"]

这也与之前的模式相同。 ..{1}是一回事。事实上,{1}总是可以从任何正则表达式中删除而不会改变任何内容。

> %W{fin\n fi\n\n \n\n fin\r\n find}.grep /f.(m:.)\Z/
=> []

您将?放在此模式中,更改了(m:.)的含义。这不再改变选项。现在它只是一个匹配模式m:.的捕获组,当然这不会出现在您的输入中。

> %W{fin\n fi\n\n \n\n fin\r\n find}.grep /f.(?m:.)\z/
=> []

您在此处将\Z更改为\z。这两者之间的差异\Z可能在尾随换行符之前匹配,但\z必须仅匹配字符串的结尾。如果无法在尾随换行符之前匹配,则此处的输入都不匹配。但是,例如,如果您有fin(没有新行)或fi\n(没有第二个换行符),那么它就会有用。