【问题标题】:Regex: I want this AND that AND that... in any order正则表达式:我想要这个和那个和那个......以任何顺序
【发布时间】:2011-04-01 19:06:29
【问题描述】:

我什至不确定这是否可能,但这就是我想要的。

String: "NS306 FEBRUARY 20078/9/201013B1-9-1Low31 AUGUST 19870"

我有一个文本框,我在其中输入搜索参数,它们以空格分隔。因此,我想返回一个匹配是 string1 在字符串中,然后 string2 在字符串中,或​​者 string2 在字符串中,然后 string1 在字符串中。我不在乎字符串的顺序,但它们都必须在字符串中(我会超过 2 个)。

例如,在我想要的提供的字符串中:

"FEB Low"

"Low FEB"

...作为匹配项返回。

我对正则表达式真的很陌生,只阅读了here 上的一些教程,但那是不久前的事了,我今天需要完成这项工作。星期一我开始了一个更重要的新项目,不能因为这个问题而分心。无论如何要使用正则表达式来做到这一点,还是我必须遍历搜索过滤器的每个部分并排列顺序?非常感谢任何和所有帮助。谢谢。

更新: 我不想遍历循环并寻找最佳性能的原因是不幸的是,我使用的 dataTable 在每次按键时都会调用此函数,我不希望它陷入困境。

更新: 谢谢大家的帮助,非常感谢。

代码更新:

最终,这就是我的选择。

string sSearch = nvc["sSearch"].ToString().Replace(" ", ")(?=.*");
if (sSearch != null && sSearch != "")
{
  Regex r = new Regex("^(?=.*" + sSearch + ").*$", RegexOptions.IgnoreCase);
  _AdminList = _AdminList.Where<IPB>(
                                       delegate(IPB ipb)
                                       {
                                          //Concatenated all elements of IPB into a string
                                          bool returnValue = r.IsMatch(strTest); //strTest is the concatenated string
                                          return returnValue;
                                    }).ToList<IPB>();
                                       }
}

IPB 类有 X 个元素,并且在我正在处理的整个站点中没有一个表中的列的顺序相同。因此,我需要进行任何订单搜索,并且我不想编写大量代码来完成它。这里还有其他好主意,但我知道我的老板真的很喜欢 Regex(宣扬它们),因此我认为最好现在就这样做。如果由于某种原因该站点的性能下滑(Intranet 站点),那么我将尝试另一种方式。谢谢大家。

【问题讨论】:

    标签: c# regex


    【解决方案1】:

    你可以使用(?=…)positive lookahead;它断言可以匹配给定的模式。您将锚定在字符串的开头,并以任意顺序逐个查找每个模式的匹配项。

    它看起来像这样:

    ^(?=.*one)(?=.*two)(?=.*three).*$
    

    这将匹配包含"one""two""three" 的字符串,顺序不限 (as seen on rubular.com)。

    根据上下文,您可能希望在\A\Z 上使用anchor,并使用单行模式,以便the dot 匹配所有内容。

    这不是解决问题的最有效方法。最好的解决方案是解析输入中的单词并将其放入有效的集合表示等中。

    相关问题


    更实际的例子:密码验证

    假设我们想要我们的密码:

    • 包含 8 到 15 个字符
    • 必须包含大写字母
    • 必须包含小写字母
    • 必须包含数字
    • 必须包含特殊符号之一

    然后我们可以这样写一个正则表达式:

    ^(?=.{8,15}$)(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[!@#$%^&*]).*$
     \__________/\_________/\_________/\_________/\______________/
        length      upper      lower      digit        symbol
    

    【讨论】:

    • 我不太清楚您所说的“有效集合表示”是什么意思。我不能这样做: string strTest = "^(?=.*" + strSearch.Replace(" ", ")(?=.*") + ").*$" 这将使所有内容都变成一个字符串不是必须迭代吗?
    • 谢谢,工作非常顺利,1600 多条记录的等待时间不超过 2 秒。
    • @XstreamINsanity 你的测试使用了什么模式?
    • 将在一分钟内添加代码。我必须手动输入。
    • 值得一提的是 * 允许 ?=。在任意数量的字符之后匹配。在没有明确提及的情况下,我花了一些时间搜索和测试才弄清楚这一点。
    【解决方案2】:

    既然顺序无关紧要,为什么不简单地检查一下文本?

    string test = "NS306 FEBRUARY 20078/9/201013B1-9-1Low31 AUGUST 19870";
    test = test.ToUpper();
    bool match = ((test.IndexOf("FEB") >= 0) && (test.IndexOf("LOW") >= 0));
    

    你需要它来使用正则表达式吗?

    【讨论】:

    • 因为我尽可能避免迭代。我听说 Regex 引擎比任何 foreach 或 for 循环都强大,所以我想使用它。
    • @XstreamINsanity 正则表达式是 sloooooow :) 它非常适合模式匹配复杂模式并使匹配过程更容易编码......但速度不是那么快。
    • @Kelsey - 比 for 循环慢? :)
    • @XstreamINsanity 在我的示例中没有 for 循环:P 在内部,IndexOf 可能有某种类型的迭代,但在内部,Regex 也是如此,除了它有一大堆特定于语言的正则表达式细节需要处理还有。
    • 好吧,我的意思是你的例子显示了 IndexOf 的 2 个语句,当我可能有无限数量的语句时,所以我必须以某种方式迭代所有的可能性,很可能是一个 for 循环。
    【解决方案3】:

    我认为今天最方便的事情是 string.Split(' ') 搜索词,然后遍历结果以确认 sourceString.Contains(searchTerm)

    var source = @"NS306 FEBRUARY 20078/9/201013B1-9-1Low31 AUGUST 19870".ToLowerInvariant();
    var search = "FEB Low";
    
    var terms = search.Split(' ');
    
    bool all_match = !terms.Any(term => !(source.Contains(term.ToLowerInvariant())));
    

    请注意,我们使用Any() 设置短路,因此如果第一项不匹配,我们将跳过检查第二项、第三项等。


    这不是 RegEx 的一个很好的用例。获取任意数量的搜索字符串并将其转换为模式所需的字符串操作几乎肯定会否定将模式与 RegEx 引擎匹配的性能优势,尽管这可能会因您匹配的对象而异。

    您已在某些 cmets 中指出要避免循环,但 RegEx 不是一次性解决方案。不难创建逐个字符循环和步进的可怕的非执行搜索,例如臭名昭​​著的catastrophic backtracking,其中一个非常简单的匹配需要数千步才能返回false

    【讨论】:

    • 我认为我的代码中的 for 循环(无论出于何种原因,只是在思考)比正则表达式引擎在库中所做的任何事情都会对性能造成更大的损害。是的,这一切都被分割成机器代码,但我不知道正则表达式是如何编写的,只是它是由比我更聪明的人编写的(我愿意承认)。 :)
    • @XstreamINsanity 是的,很容易犯错而失去性能,但这始终是“适合工作的工具”的问题。我认为这里的共识是 RegEx 不适合这项特殊工作。最后,虽然你可能看不出有什么不同,但自己尝试两种方法并选择最有效的方法也不会太难(除了迫在眉睫的最后期限)。
    • 没错。就像我在上次更新中所说的那样,有很多很棒的想法,而且所有这些想法都可能奏效,这就是为什么我很高兴我可以把这个问题留在这里,并有可供我使用的选项。我确实尝试过你的,但它似乎没有用。我可能错过了一些东西,但是一旦我看到 Regex 选项,我就放弃了我正在做的事情。感谢您的帮助。
    【解决方案4】:

    @polygenelubricants 的答案既完整又完美,但我有一个案例,我想匹配一个日期和其他东西,例如一个 10 位数字,所以前瞻不匹配,我不能只用前瞻来做到这一点,所以我使用了命名组:

    (?:.*(?P&lt;1&gt;[0-9]{10}).*(?P&lt;2&gt;2[0-9]{3}-(?:0?[0-9]|1[0-2])-(?:[0-2]?[0-9]|3[0-1])).*)+

    这样,数字始终是第 1 组,日期始终是第 2 组。当然它有一些缺陷,但它对我非常有用,我只是想我应该分享它! (看看https://www.debuggex.com/r/YULCcpn8XtysHfmE

    【讨论】:

      【解决方案5】:
      var text = @"NS306Low FEBRUARY 2FEB0078/9/201013B1-9-1Low31 AUGUST 19870";   
      var matches = Regex.Matches(text, @"(FEB)|(Low)");
      foreach (Match match in matches) {
          Console.WriteLine(match.Value);
      }
      

      输出:

      Low
      FEB
      FEB
      Low
      

      应该让你开始

      【讨论】:

      • 是的,我有,但我想尽可能避免 for 循环,认为它会影响性能。
      • @XstreamINsanity。该循环仅用于将它们打印出来。匹配集合具有正则表达式的结果,它只执行一次。
      【解决方案6】:

      您不必测试每个排列,只需将搜索分成“FEB”和“Low”多个部分并确保每个部分都匹配。这比尝试一次性想出一个匹配整个事情的正则表达式要容易得多(我敢肯定这在理论上是可能的,但在现实中可能并不实用)。

      【讨论】:

      • 这可能是我的一种方式。但是,我真的很想看看是否有正则表达式解决方案,因为这是我的理解,我希望我是正确的,不要听起来很愚蠢,正则表达式引擎能够比我迭代更快地找到它通过数组中的字符串。
      【解决方案7】:

      使用 string.Split()。它将返回一个由指定字符串/字符重新分隔的子字符串数组。代码将如下所示。

      int maximumSize = 100;
      string myString = "NS306 FEBRUARY 20078/9/201013B1-9-1Low31 AUGUST 19870";
      string[] individualString = myString.Split(' ', maximumSize);

      更多信息 http://msdn.microsoft.com/en-us/library/system.string.split.aspx

      编辑: 如果您真的想使用正则表达式,则此模式将起作用。 [^ ]* 你将只使用 Regex.Matches(); 代码将是这样的:

      string myString = "NS306 FEBRUARY 20078/9/201013B1-9-1Low31 AUGUST 19870";
      string pattern = "[^ ]*"; Regex rgx = new Regex(pattern);
      foreach(Match match in reg.Matches(s))
      {
      //do stuff with match.value
      }

      【讨论】:

      • 拆分很容易,我试图不通过循环重复。
      猜你喜欢
      • 1970-01-01
      • 2023-03-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多