【问题标题】:Best way to parse a string into Dictionary of terms将字符串解析为术语字典的最佳方法
【发布时间】:2014-07-10 07:02:12
【问题描述】:

输入 - 字符串:"TAG1xxxTAG2yyyTAG3zzzTAG1tttTAG1bbb"

预期结果:对TAG1 = {xxx,,ttt,bbb}, TAG2 = {yyy}, TAG3 = {zzz}.

我是使用正则表达式完成的,但我对使用 Regex.Replace 而不是使用返回值感到非常困惑。我想改进这段代码,如何实现呢?

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace TermsTest
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] tags = { "TAG1", "TAG2", "TAG3", "TAG4", "TAG5", "TAG6", "TAG7", "TAG8" };
            string file = "TAG2jjfjfjndbfdjTAG1qqqqqqqTAG3uytygh fhdjdfTAG5hgjdhfghTAG6trgfmxc hdfhdTAG2jfksksdhjskTAG3kdjbjvbsjTAG2jskjdjdvjvbxjkvbjdTAG2jkxcndjcjbkjn";

            string tag = "(" + string.Join("|", tags) + ")";

            var dictionary = new Dictionary<string, List<string>>(tags.Length);
            Regex.Replace(file, string.Format(@"({0})(.+?)(?={0}|$)", tag), match =>
                                                                            {
                                                                                string key = match.Groups[1].Value, value = match.Groups[3].Value;
                                                                                if (dictionary.ContainsKey(key))
                                                                                    dictionary[key].Add(value);
                                                                                else
                                                                                    dictionary[key] = new List<string> {value};
                                                                                return "";
                                                                            });
            foreach (var pair in dictionary)
            {
                Console.Write(pair.Key + " =\t");
                foreach (var entry in pair.Value)
                {
                    Console.Write(entry + " ");
                }
                Console.WriteLine();
                Console.WriteLine();
            }
        }
    }
}

【问题讨论】:

    标签: c# .net regex optimization refactoring


    【解决方案1】:
    string input = "TAG1xxxTAG2yyyTAG3zzzTAG1tttTAG1bbb";
    var lookup = Regex.Matches(input, @"(TAG\d)(.+?)(?=TAG|$)")
                        .Cast<Match>()
                        .ToLookup(m => m.Groups[1].Value, m => m.Groups[2].Value);
    
    foreach (var kv in lookup)
    {
        Console.WriteLine(kv.Key + " => " + String.Join(", ", kv));
    }
    

    输出:

    TAG1 => xxx, ttt, bbb
    TAG2 => yyy
    TAG3 => zzz
    

    【讨论】:

    • 谢谢,看起来好多了!
    【解决方案2】:

    你想要做的只是对同一标签的值进行分组,所以使用GroupBy方法应该更容易:

    string input = "TAG1xxxTAG2yyyTAG3zzzTAG1tttTAG1bbb";
    var list = Regex.Matches(input, @"(TAG\d+)(.+?)(?=TAG\d+|$)")
                    .Cast<Match>()
                    .GroupBy(m => m.Groups[1].Value,
                             (key, values) => string.Format("{0} = {{{1}}}", 
                                                 key, 
                                                 string.Join(", ", 
                                                    values.Select(v => v.Groups[2]))));
    var output = string.Join(", ", list);
    

    这会产生一个output 字符串"TAG1 = {xxx, ttt, bbb}, TAG2 = {yyy}, TAG3 = {zzz}"

    【讨论】:

      【解决方案3】:

      我不确定我是否了解您在这个问题中的所有假设和约定;但这给了我类似的结果:

      var tagColl = string.Join("|", tags);
      var tagGroup = string.Format("(?<tag>{0})(?<val>[a-z]*)", tagColl);
      
      var result = from x in Regex.Matches(file, tagGroup).Cast<Match>()
                      where x.Success
                      let pair = new { fst = x.Groups["tag"].Value, snd = x.Groups["val"].Value }
                      group pair by pair.fst into g
                      select g;
      

      一个简单的测试是:

      Console.WriteLine(string.Join("\r\n", from g in result
                                              let coll = string.Join(", ", from item in g select item.snd)
                                              select string.Format("{0}: {{{1}}}", g.Key, coll)));
      

      【讨论】:

        【解决方案4】:

        这对于 .NET CaptureCollection 对象来说是一项完美的工作——这是一项独特的 .NET 功能,可让您多次重复使用同一个捕获组。

        使用此正则表达式并使用 Matches 创建 MatchCollection:

        (?:TAG1(.*?(?=TAG|$)))?(?:TAG2(.*?(?=TAG|$)))?(?:TAG3(.*?(?=TAG|$)))?
        

        然后检查捕获:

        • Groups[1].Captures 将包含所有 TAG1
        • Groups[2].Captures 将包含所有 TAG2
        • Groups[3].Captures 将包含所有 TAG3

        从那里到您的最终数据结构只有一小步。

        为了减少回溯的可能性,您可以使标记原子化:

        (?>(?:TAG1(.*?(?=TAG|$)))?)(?>(?:TAG2(.*?(?=TAG|$)))?)(?>(?:TAG3(.*?(?=TAG|$)))?)
        

        有关其工作原理的详细信息,请参阅Capture Groups that can be Quantified

        【讨论】:

        • 对于例如 100 个标签来说似乎很慢。还是我错了?
        • 好的,让我们通过使组原子化来减少回溯的可能性:(?&gt;(?:TAG1(.*?(?=TAG|$)))?)(?&gt;(?:TAG2(.*?(?=TAG|$)))?)(?&gt;(?:TAG3(.*?(?=TAG|$)))?)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多