正则表达式:多个匹配前的文本

时间:2015-09-11 12:10:31

标签: ruby regex

即可。给定字符串,返回所有匹配(带有重叠)和这些匹配之前的文本。

示例即可。对于文本atatgcgcatatat和查询atat,有三个匹配项,所需的输出为atatatatgcgcatatatatgcgcatatat

问题即可。我使用Ruby 2.2和String#scan方法来获得多个匹配项。我尝试使用lookahead,但正则表达式/(?=(.*?atat))/返回以atat结尾的每个子字符串。必须有一些正则表达式的魔法来解决这个问题,但我无法弄清楚正确的法术。

4 个答案:

答案 0 :(得分:4)

我相信这至少比OP的回答更好:

text = "atatgcgcatatat"
query = "atat"

res = []
text.scan(/(?=#{query})/){res.push($` + query)}                                  #`
res # => ["atat", "atatgcgcatat", "atatgcgcatatat"]

答案 1 :(得分:3)

鉴于正则表达式的性质和目的,没有办法做到这一点。当正则表达式匹配文本时,无法在另一个匹配项中包含相同的文本。因此,我能想到的最佳选择是使用后视来查找每场比赛的结束位置:

(?<=atat)

使用atatgcgcatatat的示例输入,将返回以下三个匹配项:

  • 位置4,长度0
  • 位置12,长度0
  • 位置14,长度0

然后,您可以遍历这些结果,获取每个结果的位置,然后获取从输入字符串的开头开始并在该位置结束的子字符串。如果您不知道如何获得每场比赛的位置,您可能会发现this question的答案很有帮助。

答案 2 :(得分:1)

你可以这样做:

str = 'atatgcgcatatat'
target = 'atat'

[].tap do |a|
  str.gsub(/(?=#{target})/) { a << str[0, $~.end(0)+target.size] }
end
  #=> ["atat", "atatgcgcatat", "atatgcgcatatat"]

请注意gsub返回的字符串将被丢弃。

答案 3 :(得分:0)

看来,没有办法一次性解决问题。

一种可能的解决方案是使用Float#==在使用String#scan时获取匹配索引,然后返回切片字符串数组:

def find_by_end text, query
    res = []
    n = query.length
    text.scan( /(?=(#{query}))/ ) do |m|
        res << text.slice(0, $~.offset(0).first + n)
    end
    res
end

find_by_end "atatgcgcatatat", "atat" #=> ["atat", "atatgcgcatat", "atatgcgcatatat"]

@StevenDoggart提出了一个略有不同的解决方案。这是一个很好的短代码,使用this knowledge hack来解决问题:

"atatgcatatat".to_enum(:scan, /(?<=atat)/).map { $` }                         #`
#=> ["atat", "atatgcatat", "atatgcatatat"]

正如@CasimiretHippolyte所说,反转字符串可能有助于解决问题。它确实如此,但它不是最漂亮的解决方案:

"atatgcatatat".reverse.scan(/(?=(tata.*))/).flatten.map(&:reverse).reverse
#=> ["atat", "atatgcatat", "atatgcatatat"]