使用正则表达式将字符串映射到功能

时间:2011-11-26 14:49:21

标签: .net regex string function mapping

我有一个字符串和多个正则表达式,例如一个正则表达式,它检查字符串是否只是一个数字,如果它以字符X开头,依此类推。我有不同的代码运行,具体取决于正则表达式匹配,如下所示:

if (Regex.IsMatch(myString, regex1))
{
    //number
}
else if (Regex.IsMatch(myString, regex2))
{
    //something else
}
else if (Regex.IsMatch(myString, regex3))
{
    //something else
}

等等。然而,这看起来非常笨重,因为我喜欢10个正则表达式,所以我可以使用开关/案例做同样的事情吗?如果是这样,我可以提供一个例子吗?

我正在使用.NET 2.0和WinForms。

6 个答案:

答案 0 :(得分:2)

你可以保存一个关联字典 - 匹配函数和处理逻辑,然后只需循环匹配,一旦它返回true - 执行相关的处理逻辑,这样你最终会得到非常简单的foreach循环:

string input = "some input ^&(*729384293";
string numberPattern = "regex to check numbers";
string datePattern = "regex to check date";

// KEY - match regex function
// VALUE - match handling logic
var matchesMap = new Dictionary<Func<bool>, Action>();

matchesMap.Add(
    () => Regex.IsMatch(input, numberPattern),
    () => { /*this code will be called when numer match found*/ });
matchesMap.Add(
    () => Regex.IsMatch(input, datePattern),
    () => { /*this code will be called when date match found*/ });

foreach (var match in matchesMap.Keys)
{
    if (match())
    {
        matchesMap[match].Invoke();
        break;
    }
}

答案 1 :(得分:1)

这不能像你描述的那样完成,因为switch只能用于:bool,char string,int,enum或相应的可空类型。

答案 2 :(得分:1)

执行此操作的唯一方法是使用switch-true语句(不推荐)。

switch (true)
{
    case Regex.IsMatch(myString, regex1):
        // ...
        break;
    case Regex.IsMatch(myString, regex2):
        // ...
        break;
    case Regex.IsMatch(myString, regex3):
        // ...
        break;
    default:
        // ...
        break;
}

编辑: Buh Buh提到了case表达式必须是常量的事实,因此代码不会编译。

还有另一种非常迂回的方法来解决这个问题。请记住,我指出这不是建议,而是作为if块更好的原因的一个例子。

如果您的正则表达式位于数组regexes中,则可以执行以下操作:

int type = -1;
for (int i = 0; i < regexes.Length; i++)
{
    if (Regex.IsMatch(myString, regexes[i])
    {
        type = i;
        break;
    }
}
switch (type)
{
    case 0:
        // ...
        break;
    case 1:
        // ...
        break;
    case 2:
        // ...
        break;
    default:
        break;
}

答案 3 :(得分:1)

你可以制作一个更复杂的正则表达式,如/(a)|(b)|(c)/然后检查match.Groups,之后匹配的模式:

Match match = Regex(input, @"/(a)|(b)|(c)/");
Foreach (Group g in match.Groups) {
  if (g.Success) {
    // Your code here
  }
}

答案 4 :(得分:1)

一个'非笨重'的解决方案是使用Chain Of Responsibility设计模式的简化版本。事实上,这种模式是为了处理大卫在这里描述的情况。

代码

在您的情况下,您只需要将字符串映射到活动。您可以定义一个基类 迭代链并在找到匹配时执行定义的活动,或者将输入转发到下一个选项。

abstract class Option
{
    public void DoActivity(string input)
    {
        Match parse = m_regex.Match(input);
        if (parse.Success)
            Activity(input);
        else if (m_nextOption != null)
            m_nextOption.DoActivity(input);
        else
            Console.WriteLine("No activity for input \"{0}\"", input);
    }

    // Each option defines its own activity; override as necessary.
    protected void Activity(string input)
    {
        Console.WriteLine(
            "For input \"{0}\" --> do activity defined in {1} ...", input, this);
    }

    // The next option in the chain, or null.
    protected Option m_nextOption;

    // Regular expression that matches the input for this option.
    protected Regex m_regex;
}

然后根据需要为多个选项创建派生类。每个定义:

  1. 与要处理的输入相匹配的正则表达式
  2. 要匹配输入的活动(在这个简单示例中都使用基类方法)和
  3. 下一个尝试链的选项,如果有的话。
  4. 类似的东西:

    internal class OptionOne : Option
    {
        public OptionOne()
        {
            m_nextOption = new OptionTwo(); // next option.
            m_regex = new Regex(@"^Option One$", RegexOptions.Compiled);
        }
    }
    internal class OptionTwo : Option
    {
        public OptionTwo()
        {
            m_nextOption = new OptionThree(); // next option.
            m_regex = new Regex(@"^Option Two$", RegexOptions.Compiled);
        }
     }
    internal class OptionThree : Option
    {
        public OptionThree()
        {
            m_nextOption = null; // no other options.
            m_regex = new Regex(@"^Option Three$", RegexOptions.Compiled);
        }
    }
    

    用法

     // Possible input strings.
     string[] myStrings = { "Option One", "Option Two",
                            "Option Three", "Matches No Options" };
    
     // Do each input's activity ...
     Option options = new OptionOne();
     foreach (var myString in myStrings)
     {
         options.DoActivity(myString);
     }
    

    输出

      

    输入“选项一” - &gt;在StackOverflow.OptionOne中定义活动...
      对于输入“选项二” - &gt;在StackOverflow.OptionTwo中定义的活动...
      输入“选项三” - &gt;在StackOverflow.OptionThree中定义活动...
      没有输入活动“匹配无选项”

    使用此模式的另一个好处是,如果以后需要添加其他选项,则只需定义其他派生类并将其挂钩到链中。调用代码根本不需要改变!

答案 5 :(得分:1)

归结为每个Regex案例的代码量。

如果它只是一行或两行代码

我说坚持使用if-else。它不仅更直接,更直观,而且更容易阅读。


如果它很复杂,或者您多次使用开关

如果您拥有的内容相当复杂,您可能需要考虑实施战略模式或类似的东西。


底线

有时候switch语句很好,但是当你发现自己多次使用同一个开关,或者每种情况都有大量的代码时,策略模式可以真正有所帮助,特别是在可维护性方面