如何在字符串中获得可能重叠的匹配
我正在寻找一种方法,无论是在Ruby还是Javascript中,它都会在字符串中为正则表达式提供所有匹配,可能重叠。
假设我有str = "abcadc"
,我想找到a
后跟任意数量的字符,然后是c
。 我正在寻找的结果是["abc", "adc", "abcadc"]
。 关于如何实现这一目标的任何想法?
str.scan(/a.*c/)
会给我["abcadc"]
, str.scan(/(?=(a.*c))/).flatten
会给我["abcadc", "adc"]
。
def matching_substrings(string, regex) string.size.times.each_with_object([]) do |start_index, maching_substrings| start_index.upto(string.size.pred) do |end_index| substring = string[start_index..end_index] maching_substrings.push(substring) if substring =~ /^#{regex}$/ end end end matching_substrings('abcadc', /a.*c/) # => ["abc", "abcadc", "adc"] matching_substrings('foobarfoo', /(\w+).*\1/) # => ["foobarf", # "foobarfo", # "foobarfoo", # "oo", # "oobarfo", # "oobarfoo", # "obarfo", # "obarfoo", # "oo"] matching_substrings('why is this downvoted?', /why.*/) # => ["why", # "why ", # "why i", # "why is", # "why is ", # "why is t", # "why is th", # "why is thi", # "why is this", # "why is this ", # "why is this d", # "why is this do", # "why is this dow", # "why is this down", # "why is this downv", # "why is this downvo", # "why is this downvot", # "why is this downvote", # "why is this downvoted", # "why is this downvoted?"]
在Ruby中,您可以使用以下方法获得预期结果:
str = "abcadc" [/(a[^c]*c)/, /(a.*c)/].flat_map{ |pattern| str.scan(pattern) }.reduce(:+) # => ["abc", "adc", "abcadc"]
这种方式是否适合您,高度依赖于您真正想要实现的目标。
我试着把它放到一个单独的表达式中,但我无法使它工作。 我真的想知道是否有一些科学原因,这不能通过正则表达式解析,或者我只是不太了解Ruby的解析器Oniguruma来做到这一点。
您想要所有可能的匹配,包括重叠匹配。 正如您所指出的那样,“ 如何找到与正则表达式重叠匹配? ”的前瞻技巧对您的情况不起作用。
在一般情况下,我唯一能想到的就是生成字符串的所有可能的子串,并根据正则表达式的锚定版本检查每个子串。 这是蛮力,但它的作用。
ruby:
def all_matches(str, regex) (n = str.length).times.reduce([]) do |subs, i| subs += [*i..n].map { |j| str[i,ji] } end.uniq.grep /^#{regex}$/ end all_matches("abcadc", /a.*c/) #=> ["abc", "abcadc", "adc"]
使用Javascript:
function allMatches(str, regex) { var i, j, len = str.length, subs={}; var anchored = new RegExp('^' + regex.source + '$'); for (i=0; i
在JS中:
function doit(r, s) { var res = [], cur; r = RegExp('^(?:' + r.source + ')$', r.toString().replace(/^[\s\S]*\/(\w*)$/, '$1')); r.global = false; for (var q = 0; q < s.length; ++q) for (var w = q; w <= s.length; ++w) if (r.test(cur = s.substring(q, w))) res.push(cur); return res; } document.body.innerHTML += "" + JSON.stringify(doit( /a.*c/g, 'abcadc' ), 0, 4) + "
";
▶ str = "abcadc" ▶ from = str.split(/(?=\p{L})/).map.with_index { |c, i| i if c == 'a' }.compact ▶ to = str.split(/(?=\p{L})/).map.with_index { |c, i| i if c == 'c' }.compact ▶ from.product(to).select { |f,t| f < t }.map { |f,t| str[f..t] } #⇒ [ # [0] "abc", # [1] "abcadc", # [2] "adc" # ]
我相信,有一种奇特的方法来查找字符串中字符的所有索引,但我无法找到它:(任何想法?
拆分“unicode char boundary”可以使用'ábĉ'
或'Üve Østergaard'
等字符串。
对于更通用的解决方案,它接受任何“from”和“to”序列,应该引入一点修改:在字符串中查找“from”和“to”的所有索引。
这是一种类似于@ndn和@ Mark的方法,适用于任何字符串和正则表达式。 我已经将它实现为String
一种方法,因为这是我希望看到它的地方。 对String#[]
和String#scan
不是很好的恭维吗?
class String def all_matches(regex) return [] if empty? r = /^#{regex}$/ 1.upto(size).with_object([]) { |i,a| a.concat(each_char.each_cons(i).map(&:join).select { |s| s =~ r }) } end end 'abcadc'.all_matches /a.*c/ # => ["abc", "abcadc", "adc"] 'aaabaaa'.all_matches(/a.*a/) #=> ["aa", "aa", "aa", "aa", "aaa", "aba", "aaa", "aaba", "abaa", "aaaba", # "aabaa", "abaaa", "aaabaa", "aabaaa", "aaabaaa"]
RegExp
/(ac)|(a.*c)/g
是匹配"a"
字符后跟任何字符后跟"c"
; "a.*c"
是匹配"a"
后跟任何字符后跟前面的字符后跟"c"
字符; 注意(a.*c)
RegExp
可能会得到改善。 条件为if
检查输入字符串中的最后一个字符是否为"c"
,如果为true
,则将完整输入字符串推送到res
结果数组
var str = "abcadc" , res = str.match(/(ac)|(a.*c)/g); if (str[str.length - 1] === "c") res.push(str); document.body.textContent = res.join(" ")