【问题标题】:Linq Pattern MatchingLinq 模式匹配
【发布时间】:2009-10-18 14:58:55
【问题描述】:

我正在使用正则表达式来匹配模式,例如在以下示例中我正在匹配字符串以计算元音。

void VowelsCountInEachWord()
{
  Regex rx = new Regex("[aeiou]");
  var words=new string[] 
                         {"aesthetic", "benevolent", "abstract",
                           "capricious", "complacent", "conciliatory",
                           "devious", "diligent", "discernible","dogmatic",
                           "eccentric","fallacious","indifferent","inquisitive",
                           "meticulous","pertinent","plausible", "reticent"
                         };

  var filter = from w in words where (rx.IsMatch(w.ToLower())) select new  

              {w,count=VowelsCounting(w)};


 foreach (var v in filter)
 {
     Console.WriteLine("String {0} contains {1} vowels", v.w, v.count);
 }

}

 public int VowelsCounting(string value)
 {

    int cnt=0;
    foreach (char c in value)
    {
         switch (c)
         {
              case 'a':cnt++;break;
              case 'e':cnt++;break;
              case 'i':cnt++;break;
              case 'o':cnt++;break;
              case 'u':cnt++;break;
           }
     }
            return cnt++;
  }

1) 在不使用正则表达式的情况下,C# 是否提供任何结构来匹配模式?

2) 对于对字符串计数单个字符,我需要派生自己的方法吗?

【问题讨论】:

    标签: c# linq pattern-matching


    【解决方案1】:

    1) 不使用正则表达式 C# 是否提供任何构造 匹配模式?

    没有什么比正则表达式更强大的了,它可以一次性为您提供一切。

    2) 用于计算单个字符 我需要对字符串进行推导吗 我自己的方法?

    我不推荐这种方法,但我说它只是为了表明您可以使用一些内置方法来实现它。您可以想象使用String.IndexOf 并从索引 0 开始查找“a”,并在循环中不断前进,同时在正匹配时递增计数器。然后重复 for "e"..."u" 但它的效率将远低于正则表达式或 for 循环。

    更好的方法是逐个字符循环遍历字符串,然后将其提供给现有的 switch 语句或在某个集合中查找。

    由于您想使用 LINQ,因此可以重写上述 for 循环的想法以使其适合。请注意,这个想法类似于HuBeZa's solution,所以在那里 +1。但是,我使用列表进行查找并使用StringComparison.InvariantCultureIgnoreCase 枚举来忽略大小写:

    var vowels = new List<string> { "a", "e", "i", "o", "u" };
    var query = words.Select(s => new
                {
                    Text = s,
                    Count = s.Count(c => vowels.Exists(vowel => 
                        vowel.Equals(c.ToString(), 
                            StringComparison.InvariantCultureIgnoreCase)))
                });
    foreach (var item in query)
    {
        Console.WriteLine("String {0} contains {1} vowels", item.Text, item.Count);
    }
    

    我的原始正则表达式响应如下。


    正则表达式方法

    有一个比您正在使用的更好的正则表达式解决方案。我不确定你是否知道,所以我觉得有必要发一个帖子。在问题 #1 中,您说“不使用正则表达式”,但恕我直言,这与问题 #2 直接冲突,您询问是否必须派生自己的方法。

    您可以通过在返回的 MatchCollection 上使用 Regex.Matches methodCount property 来缩短代码:

    Regex rx = new Regex("[aeiou]");
    // to ignore case use: new Regex("[aeiou]", RegexOptions.IgnoreCase);
    string[] words =
    {
        "aesthetic", "benevolent", "abstract",
        "capricious", "complacent", "conciliatory",
        "devious", "diligent", "discernible","dogmatic",
        "eccentric","fallacious","indifferent","inquisitive",
        "meticulous","pertinent","plausible", "reticent"
    };
    
    foreach (string input in words)
    {
        Console.WriteLine("String {0} contains {1} vowels",
            input, rx.Matches(input).Count);
    }
    
    // if you really want to use LINQ
    var query = from s in words
                select new
                {
                    Text = s,
                    Count = rx.Matches(s).Count
                };
    foreach (var item in query)
    {
        Console.WriteLine("String {0} contains {1} vowels", item.Text, item.Count);
    }
    

    顺便说一句,您可以通过更改 2 项来进一步缩短原始代码:

    1) 字符串数组声明(我在上面的示例中已经这样做了)
    2)让你的案例陈述落入下一个案例标签:

    switch (c)
    {
        case 'a':
        case 'e':
        case 'i':
        case 'o':
        case 'u':
            cnt++;
            break;
    }
    

    编辑: 使用 LINQ 查询进行了更新。它与 OP 所拥有的没什么不同,只是使用了 Matches 方法。

    【讨论】:

    • 语法缩短的优点。您还可以使用 LINQ Select 方法删除 foreach:words.Select(input => new { input, Count = rx.Matches(input).Count });
    • @HuBeZa:谢谢!正如您评论的那样,我使用类似的 LINQ 查询更新了我的代码:) 当然,您的代码使用 lamda 查询更紧凑;我正在匹配 OP 的理解查询。
    • +0.5 表示最详细的答案,另一个 +0.5 表示忽略大小写比较
    【解决方案2】:

    你的解决方案还不错,但如果你坚持:

    var vowels = new char[] { 'a', 'e', 'i', 'o', 'u' };
    var words = new string[] 
                 {"aesthetic", "benevolent", "abstract",
                   "capricious", "complacent", "conciliatory",
                   "devious", "diligent", "discernible","dogmatic",
                   "eccentric","fallacious","indifferent","inquisitive",
                   "meticulous","pertinent","plausible", "reticent"
                 };
    
    var filter =
        (from w in words
        select new { w, count = w.ToLower().Count(c => vowels.Contains(c)) }).
            Where(item => item.count > 0);
    

    编辑:正如这里的一些建议,我删除了 ToCharArray,我添加了 ToLower 检查和零元音过滤器。

    【讨论】:

    • 你不需要 ToCharArray() 字符串实现 IEnumerable
    • @Akash:我认为这很明显。我添加了零元音过滤。 @Yuriy:你说得对,我可以使用没有显式转换的扩展方法吗?请发布代码示例。
    • @HuBeZa:你的代码很好,只需要一点样式和更少的变量(在我看来)
    【解决方案3】:

    以下解决方案将仅包含包含元音的项目,并且它将是最快的解决方案。

    var filter = from w2 in
                     (from w in words
                      select new
                      {
                          w,
                          count =
                              w.ToLower().ToCharArray().Count(
                                c => c == 'a' || 
                                    c == 'e' || 
                                    c == 'i' || 
                                    c == 'o' || 
                                    c == 'u')
                      })
                 where (w2.count > 0)
                 select w2; 
    

    【讨论】:

    • 很好,但不需要调用ToCharArray,因为string 实现了IEnumerable&lt;char&gt;。但是,IEnumerable&lt;T&gt; 扩展方法未在 Visual Studio 中公开,原因很可疑:stackoverflow.com/questions/345883/…
    • @JulianR ,哦,我后来看到了,以为 VS 隐藏了它,所以它不存在 :)
    • @JulianR:感谢您提供的启发性链接。可疑的是这个“功能”的确切描述。
    • PS:这个方法可以通过删除ToLower并简单地添加大写的比较来加快4倍。
    • 谢谢朱利安,我只是想展示一下我猜请求者应该为自己做更多工作的方式,因为我不会得到任何报酬:)
    【解决方案4】:

    每个人都把这件事弄得太复杂了:

    Regex rx = new Regex("[aeiou]", RegexOptions.IgnoreCase);
    
    var query = from w in words
                let vowelCount = rx.Matches(w).Count
                where vowelCount != 0
                select new { w, vowelCount };
    

    当然,如果您不需要包含元音的单词列表(哪些单词没有?)并且您只想输出计数,那么使用循环会更容易。

    【讨论】:

    • 不是所有人...这是我在回复中使用的方法:stackoverflow.com/questions/1585085/linq-pattern-matching/…
    • 不,不是每个人都在制造这种复杂性。我问了这个问题,没有正则表达式,我可以处理它。这就是他们给出答案的原因。非常感谢乔伦。
    • 哦,你说得对,艾哈迈德,我因为某种原因错过了你的帖子。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-17
    • 2022-01-12
    相关资源
    最近更新 更多