Ruby正则表达式,仅捕获一个匹配项(非常简单!)

6
我想这可能是一个愚蠢的错误,但对我来说,以下代码只返回一个包含 "M" 的数组。请看这里: ```html

我想这可能是一个愚蠢的错误,但对我来说,以下代码只返回一个包含 "M" 的数组。请看这里:

```
/(.)+?/.match("Many many characters!").captures
=> ["M"]

为什么它不返回每个字符的数组?我一定错过了什么非常明显的东西,因为我看不出有什么问题?
编辑:刚意识到,我不需要+?但是没有它仍然无法正常工作。
编辑:抱歉!我会澄清一下:我的目标是允许用户输入一个正则表达式和样式以及一个输入文本文件,在任何匹配的地方,文本将被包围在一个html元素中,并应用样式,我不仅仅是将字符串拆分成字符,我只使用了给定的正则表达式,因为它是最简单的,尽管这是我愚蠢的行为。如何从scan()获取捕获组,或者这不可能吗?我看到$1包含“!”(最后一个匹配?),而不是其他任何内容。
编辑:天哪,今天真不是我的日子。正如injekt告诉我一样,捕获存储在单独的数组中。如何从原始字符串获取这些捕获的偏移量?我想能够获取捕获的偏移量,然后用另一个字符串将其包围。或者这就是gsub的作用?(我认为它只替换了匹配项,而不是捕获组)
希望这是最后一次编辑:好吧,让我重新开始吧:P
所以,我有一个字符串。用户将使用配置文件输入正则表达式,然后与每个捕获组相关联的样式。我需要能够扫描整个字符串,并获取每个组匹配的开始和结束或偏移量和大小。
因此,如果用户配置了([\w-\.]+)@((?:[\w]+\。)+)([a-zA-Z]{2,4})(电子邮件地址),那么我应该能够获取:
[ ["elliotpotts", 0,  11],
  ["sample.",     12, 7],
  ["com",         19, 3] ]

从字符串中提取:"elliotpotts@sample.com"

如果不清楚,请原谅我 :P。非常感谢大家迄今为止的帮助,感谢您的耐心!


我刚看到你的编辑,从扫描中捕获的组存储在单独的数组中,只需在irb中尝试您的正则表达式和测试字符串,您将看到。答案仍然与您包含的编辑相同。 - Lee Jarvis
刚刚看到你的下一个编辑,你需要更新更多信息。现在我有点困惑 :P 随意提供一个更完整的示例,不管它有多么牵强,这样我们就知道你需要提取什么信息了。 - Lee Jarvis
好的,我已经根据你最新的编辑更新了我的答案。我现在有点时间紧迫,所以只提供完整的解决方案而没有解释,请让我知道如果不清楚,我会再更新它。 - Lee Jarvis
4个回答

9
因为你的匹配只能匹配一个单一字符。(.)+(.+)是不同的。
>> /(.)+?/.match("Many many characters!").captures
=> ["M"]
>> /(.+)?/.match("Many many characters!").captures
=> ["Many many characters!"]
>> /(.+?)/.match("Many many characters!").captures
=> ["M"]

如果你想要递归地匹配每个字符,请使用String#scan,如果你不关心捕获组,可以使用String#split 使用scan
"Many many characters!".scan(/./)
#=> ["M", "a", "n", "y", " ", "m", "a", "n", "y", " ", "c", "h", "a", "r", "a", "c", "t", "e", "r", "s", "!"]

请注意,其他答案使用(.),如果您关心捕获组,那么这是可以的,但如果不关心,那么它就有点无意义了,否则它将返回每个字符在自己单独的数组中,如下所示:
[["M"], ["a"], ["n"], ["y"], [" "], ["m"], ["a"], ["n"], ["y"], [" "], ["c"], ["h"], ["a"], ["r"], ["a"], ["c"], ["t"], ["e"], ["r"], ["s"], ["!"]]

否则,只需要使用 split"Many many characters!".split(' ') 编辑 针对您的编辑回复:
reg = /([\w-\.]+)@((?:[\w]+\.)+)([a-zA-Z]{2,4})/
str = "elliotpotts@sample.com"
str.scan(reg).flatten.map { |capture| [capture, str.index(capture), capture.size] }
#=> [["elliotpotts", 0, 11], ["sample.", 12, 7], ["com", 19, 3]]`

噢,而且你不需要用scan,因为你并没有真正的扫描,所以你不需要遍历,至少不需要用你提供的例子来遍历:

str.match(reg).captures.map { |capture| [capture, str.index(capture), capture.size] }

也会起作用


谢谢!我还找到了另一种解决方案,现在会发布它。谢谢! - Ell
给出的两个代码片段在一般情况下对偏移量的处理不正确,只有当匹配的子字符串都不同才能正常工作。例如,如果有3个匹配项为“h”,则相同的索引(即'h'的第一个实例)将被返回3次。str.index(capture) 返回捕获子字符串的第一个实例的索引。 - jpw

1

是的,有些重要的东西被忽略了 ;-)

(...) 只引入了一个捕获组:组匹配的次数并不重要,因为索引是由正则表达式本身而不是输入决定的。

关键是使用“全局正则表达式”,它将按顺序多次应用正则表达式。在 Ruby 中,可以通过从 Regex#match 切换到 String#scan 来实现(许多其他语言都有 "/g" 正则表达式修饰符):

"Many many chara­cters!".sc­an(/(.)+?/­)
# but more simply (or see answers using String#split)
"Many many chara­cters!".sc­an(/(.)/­)

编程愉快


0

它只返回一个字符,因为这是你要求它匹配的全部内容。你可能想使用scan代替:

str = "Many many characters!"
matches = str.scan(/(.)/)

0
以下代码来自在Ruby中获取字符串扫描结果的索引,并根据我的喜好进行了修改。
[].tap {|results|
    "abab".scan(/a/) {|capture|
        results.push(([capture, Regexp::last_match.offset(0)]).flatten)
    }
}

=> [["a", 0], ["a", 2]]

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