如何在字符串中获取可能重叠的匹配项

IT技术 javascript ruby regex
2021-02-25 02:46:15

我正在寻找一种方法,无论是在 Ruby 还是 Javascript 中,它都会为我提供所有匹配项,可能是重叠的,在一个字符串中针对正则表达式。


假设我有str = "abcadc",并且我想找到a后跟任意数量字符的出现,后跟c. 我正在寻找的结果是["abc", "adc", "abcadc"]关于我如何做到这一点的任何想法?

str.scan(/a.*c/)会给我["abcadc"]str.scan(/(?=(a.*c))/).flatten会给我["abcadc", "adc"]

6个回答
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?"]
@mudasobwa,你是什么意思?假设正则表达式中没有环视,我的解决方案适用于随机正则表达式。即使有环视,问题也是可以解决的。我的反对意见是您的解决方案不能参数化正则表达式。
2021-04-18 02:46:15
我显然没有贬低,但你的反对是愚蠢的:你像我一样提供了代码片段,而不仅仅是一个神奇的正则表达式。由于显而易见的原因,这种情况下的神奇正则表达式不存在:该问题无法通过简单的状态机解决。
2021-04-28 02:46:15
我建议您删除/替换“为什么这被低估了?” 例如,它很有可能只会吸引更多的反对票。虽然此代码片段可能会解决问题,但包括解释确实有助于提高帖子的质量。请记住,您是在为将来的读者回答问题,而那些人可能不知道您提出代码建议的原因。
2021-05-05 02:46:15
@mudasobwa,您的问题没有回答(又名给定的正则表达式,获取与其匹配的子字符串)。我的初始解决方案也有同样的问题。
2021-05-11 02:46:15

在 Ruby 中,您可以使用以下方法实现预期结果:

str = "abcadc"
[/(a[^c]*c)/, /(a.*c)/].flat_map{ |pattern| str.scan(pattern) }.reduce(:+)
# => ["abc", "adc", "abcadc"]

这种方式是否适合您在很大程度上取决于您真正想要实现的目标。

我试图把它变成一个单一的表达,但我无法让它工作。我真的很想知道是否有一些科学原因无法通过正则表达式解析它,或者我对 Ruby 的解析器 Oniguruma 了解得不够多。

该解决方案可以轻松适应您的第一个示例。对于第二个,你可能是对的。我完全不知道如何适应它。这就是为什么我写这句话说这取决于 OP 确切地试图实现的目标。
2021-04-25 02:46:15
假设 OP 的字符串和正则表达式只是一个例子,这并没有给出这个问题的通用答案。
2021-05-04 02:46:15
@WilliamFeng什么是预期的结果abcadcdcshould'nt它包括abcadcadcdc
2021-05-04 02:46:15
如果是这样,请举一个不起作用的例子。
2021-05-06 02:46:15
如果问题是关于匹配/b.*d/呢?或者大约/x.*y.*z.*[^m]*foo/
2021-05-13 02:46:15

您需要所有可能的匹配项,包括重叠的匹配项。正如您所指出的,“如何使用正则表达式查找重叠匹配项? ”中的先行技巧不适用于您的情况。

在一般情况下,我能想到的唯一一件事是生成字符串的所有可能的子字符串,并根据正则表达式的锚定版本检查每个子字符串。这是蛮力,但它的工作原理。

Ruby:

def all_matches(str, regex)
  (n = str.length).times.reduce([]) do |subs, i|
     subs += [*i..n].map { |j| str[i,j-i] }
  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<len; ++i) {
    for (j=i; j<=len; ++j) {
       subs[str.slice(i,j)] = true;
    }
  }
  return Object.keys(subs).filter(function(s) { return s.match(anchored); });
}

在JS中:

function extract_ov_matches(r, s) {
  var res = [], cur;
  r = RegExp('^(?:' + r.source + ')$', r.toString().replace(/^[\s\S]*\/(\w*)$/, '$1').replace('g',''));
  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 += "<pre>" + JSON.stringify(extract_ov_matches( /a.*c/g, 'abcadc' ), 0, 4) + "</pre>";

这里的重点是您需要创建输入字符串的所有可能排列,然后返回那些完全匹配提供的模式的排列。

extract_ov_matches功能概述

  • r 是提供的正则表达式(一个编译的正则表达式对象,带有标志)
  • s 是输入字符串
  • RegExp('^(?:' + r.source + ')$', r.toString().replace(/^[\s\S]*\/(\w*)$/, '$1').replace('g',''));重新创建带有锚点的正则表达式(^用于字符串的开头和字符串$的结尾)以匹配整个字符串并g删除标志(因为正则表达式将RegExp#test一起使用
  • 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);: 如果当前排列完全匹配模式,则将其添加到res,最终将返回。
@mudasobwa:它运行良好,因为它尝试输入字符串的所有可能的子字符串。
2021-05-09 02:46:15
▶ 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 边界”上拆分使其可以处理像'ábĉ'or 之类的字符串'Üve Østergaard'

对于接受任何“from”和“to”序列的更通用的解决方案,应该引入一点修改:找到字符串中“from”和“to”的所有索引。

@CasimiretHippolytechars由于组合变音符号,我无法使用
2021-04-24 02:46:15
假设 OP 的字符串和正则表达式只是一个例子,这并没有给出这个问题的通用答案。
2021-04-28 02:46:15
在 ruby​​ 2 中,您可以使用以下方法代替 split 方法: from = str.chars.to_a.map.with_index { |c, i| i if c == 'a' }.compact
2021-04-30 02:46:15
@ndn 我不能,感谢您指出我也不能split(//)试穿一下'ábĉ'
2021-05-04 02:46:15
@ndn 它适用于 1 符号分隔符。
2021-05-12 02:46:15