查找给定字符串的所有子序列

时间:2016-03-20 19:18:01

标签: c# regex subsequence

我有一个字符串(不包含换行符),我想找到第一个字符串中的所有子序列。

例如,我们假设我要查找的字符串是"hello world"。如果我在此字符串中搜索(使用正则表达式):"1h2e3l4l5o6 7w8o9r0l0d0",它应该能够发现它确实包含(一次)字符串"hello world"。它还应该能够在以下字符串中找到多个匹配项:"hheelloo wwoorrlldd"

我有以下代码:

string stringToSearch = // could be anything (no newline)
int numOfSubSeq = 0;
Regex myRegex = new Regex("h.*e.*l.*l.*o.* .*w.*o.*r.*l.*d");
MatchCollection matches = myRegex.Matches(stringToSearch);
numOfSubSeq = matches.Count; // I only need the number of matches

这很有效。有点。如果stringToSearch"1h2e3l4l5o6 7w8o9r0l0d0",则效果很好。但是,如果stringToSearch"hheelloo wwoorrlldd",则此正则表达式只能找到一个匹配,这是错误的。

关于如何解决此问题的任何想法?

4 个答案:

答案 0 :(得分:2)

使用.NET正则表达式,你不会有任何运气。你最好的选择是为它编写一个算法。正则表达式不适合这项工作。

那就是说,你可以使用PCRE正则表达式做到这一点,我只是为了好玩而做到了这一点;)

  

免责声明:我是本文中使用的库的作者。

Fisrt,安装PCRE.NET

m_settingsDialog = new QDialog;
QWidget settingsWidget = loader.load(&file);
settingsWidget.setParent(m_settingsDialog);

然后,构建一个这样的模式:

Install-Package PCRE.NET

在PCRE用语中,h.*?e.*?l.*?l.*?o.*?\ .*?w.*?o.*?r.*?l.*?d(?C1) 事件被称为标注。它指示正则表达式引擎在匹配的这一点调用你的自定义函数。然后,您可以说该部分是否应被视为匹配。

计算调用callout函数的次数,并告诉引擎模式应该失败,这会强制它回溯。完成工作。

(?C1)

致电public static int GetMatchCount(string searchFor, string searchIn) { if (string.IsNullOrEmpty(searchFor) || string.IsNullOrEmpty(searchIn)) return 0; var patternBuilder = new StringBuilder(); foreach (var searchChar in searchFor) patternBuilder.Append(Regex.Escape(searchChar.ToString())).Append(".*?"); patternBuilder.Length -= 3; patternBuilder.Append("(?C1)"); var pattern = new PcreRegex(patternBuilder.ToString()); var count = 0; pattern.Match(searchIn, callout => { ++count; return PcreCalloutResult.Fail; }); return count; } 会返回GetMatchCount("hello world", "hheelloo wwoorrlldd")

哦,顺便说一句,如果你想真正看到字符在输入字符串中的位置,这里有一些代码:

512

这是结果:

public static void PrintMatches(string searchFor, string searchIn)
{
    if (string.IsNullOrEmpty(searchFor) || string.IsNullOrEmpty(searchIn))
        return;

    var patternBuilder = new StringBuilder();
    foreach (var searchChar in searchFor)
        patternBuilder.Append("(").Append(Regex.Escape(searchChar.ToString())).Append(").*?");

    patternBuilder.Length -= 3;
    patternBuilder.Append("(?C1)");

    var pattern = new PcreRegex(patternBuilder.ToString());
    var outputBuilder = new StringBuilder();

    Console.WriteLine(searchIn);
    pattern.Match(searchIn, callout =>
    {
        outputBuilder.Clear();
        outputBuilder.Append(' ', searchIn.Length);

        foreach (var group in callout.Match.Groups.Skip(1))
            outputBuilder[group.Index] = '^';

        Console.WriteLine(outputBuilder);

        return PcreCalloutResult.Fail;
    });
}

答案 1 :(得分:0)

当然,由于两个原因,您无法找到多于一个结果

  • 您只搜索一个字符(h而不是h*)。
  • 您根本没有任何匹配的群组

您可以将此网站用作正则表达式的测试区:https://regex101.com/r/uT8eS0/1

首先,如果您想要只有一个匹配项,则必须定义匹配组。否则你只会一直得到一场比赛。当您搜索h时,您也在搜索单个字符。如果您想匹配多个,则必须将其更改为h*h+。如果您决定使用+,则匹配一个到无限制字符。如果你选择*,它将匹配零到无限字符。

将您的模式更改为"h+.*e+.*l+.*l+.*o+.* .*w+.*o+.*r+.*l+.*d+也将匹配您发布的模式中您的字符之间的.*指令中处理的字符。 Afaik它不可能只在一个字符串中匹配不同的组。您可以在组或子组中分隔每个字符,但这会产生大量不同的组。

答案 2 :(得分:0)

我知道,我应该用C#编写这个,但是我在PERL中写了它,因为它也知道正则表达式; @时间更容易。

$_ = "hheelllloo";

sub matchmaker {
  my @blah = @_;
  if ($blah[0] =~ /[h]{$blah[1],}[e]{$blah[1],}[l]{$blah[1],}[l]{$blah[1],}[o]{$blah[1],}/) {
    $blah[1]++;
    print("deeper\n");
    matchmaker($blah[0],$blah[1]);
   } else {
     return $blah[1]-1;
  }
}

$match = matchmaker($_,1);
print("match ",$match);

它是递归函数,如果您将查看您的字符串并要求越来越多的字母匹配您要求的模式。您可以在5分钟内将其转换为c#。模式{min,max}的正则表达式语法的关键;你增加最小值并让最大值尽可能地贪婪。所以我寻找一个h,然后是一个e,然后是一个l,而不是另一个l。如果我找到所有,我会寻找2小时,2小时,2小时,2小时1和2小时......你明白了。

现在,如果您想要增强此功能并获得更多匹配,请在字符串中向前移动一个字母,然后重新运行匹配。

你可以暂时玩这个,它是否有效。不知道:))

答案 3 :(得分:0)

请查看this解决方案以解决类似的问题(使用javascript,但很容易理解)。