C#的LINQ .ToDictionary()键已经存在(C# Linq .ToDictionary

2019-06-24 12:23发布

最后编辑:我能够找到的ini文件重复的领域。 谢谢大家的帮助!

我使用正则表达式来解析ini文件和LINQ将其存储在一个字典:

样本数据:
[WindowSettings]
窗口X POS = '0'
窗口Y位置= '0'
窗口最大化=“假”
窗口名称=“Jabberwocky”

[记录]
目录= 'C:\罗塞塔石碑\日志'

编辑:这是文件实际上导致了问题: http://pastebin.com/mQSrkrcP

EDIT2:我已经收窄,由文件中的最后一节所引起:[list_first_nonprintable]

出于某种原因,我与这个被抛出此异常解析文件之一:

System.ArgumentException:具有相同键的项已被添加。

有没有什么办法,我要么找出关键原因造成的问题(这样我就可以修复文件),或者直接跳过这是造成这个关键,继续解析?

下面是代码:

try
{
    // Read content of ini file.
    string data = System.IO.File.ReadAllText(project);

    // Create regular expression to parse ini file.
    string pattern = @"^((?:\[)(?<Section>[^\]]*)(?:\])(?:[\r\n]{0,}|\Z))((?!\[)(?<Key>[^=]*?)(?:=)(?<Value>[^\r\n]*)(?:[\r\n]{0,4}))*";
    //pattern = @"
    //^                           # Beginning of the line
    //((?:\[)                     # Section Start
    //     (?<Section>[^\]]*)     # Actual Section text into Section Group
    // (?:\])                     # Section End then EOL/EOB
    // (?:[\r\n]{0,}|\Z))         # Match but don't capture the CRLF or EOB
    // (                          # Begin capture groups (Key Value Pairs)
    //  (?!\[)                    # Stop capture groups if a [ is found; new section
    //  (?<Key>[^=]*?)            # Any text before the =, matched few as possible
    //  (?:=)                     # Get the = now
    //  (?<Value>[^\r\n]*)        # Get everything that is not an Line Changes
    //  (?:[\r\n]{0,4})           # MBDC \r\n
    //  )*                        # End Capture groups";

    // Parse each file into a Dictionary.
    Dictionary<string, Dictionary<string, string>> iniFile
                    = (from Match m in Regex.Matches(data, pattern, RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline)
                       select new
                       {
                           Section = m.Groups["Section"].Value,

                           kvps = (from cpKey in m.Groups["Key"].Captures.Cast<Capture>().Select((a, i) => new { a.Value, i })
                                   join cpValue in m.Groups["Value"].Captures.Cast<Capture>().Select((b, i) => new { b.Value, i }) on cpKey.i equals cpValue.i
                                   select new KeyValuePair<string, string>(cpKey.Value, cpValue.Value)).ToDictionary(kvp => kvp.Key, kvp => kvp.Value)

                       }).ToDictionary(itm => itm.Section, itm => itm.kvps);

    return iniFile;
}
catch (ArgumentException ex)
{
    System.Diagnostics.Debug.Write(ex.ToString());
    return new Dictionary<string, Dictionary<string, string>>();
}

提前致谢。

Answer 1:

这只是意味着,当你转换成词典 -

.ToDictionary(itm => itm.Section, itm => itm.kvps);

- 有多个键(itm.Section)。 您可以使用ToLookup相反,这是一种像一本字典,但允许多个密钥。

编辑

有一对夫妇的方式来调用ToLookup。 最简单的是指定的键选择:

var lookup = 
   // ...
.ToLookup(itm => itm.Section);

这应该提供一个查找其中的关键是类集团 。 然后让查找值应该返回一个IEnumerable,其中T是匿名类型:

Group g = null;
// TODO get group
var lookupvalues = lookup[g];

如果.NET编译器不喜欢这个(有时它似乎有麻烦搞清楚各类应该是什么),你也可以指定元素选择,例如:

ILookup<string, KeyValuePair<string,string>> lookup = 
    // ...
.ToLookup(
    itm => itm.Section.Value,    // key selector
    itm => itm.kvps              // element selector
);


Answer 2:

你可以写不重复键很容易的打破自己ToDictionary方法。

public static Dictionary<K,V> ToDictionary<TSource, K, V>(
    this IEnumerable<TSource> source, 
    Func<TSource, K> keySelector, 
    Funct<TSource, V> valueSelector)
{
  //TODO validate inputs for null arguments.

  Dictionary<K,V> output = new Dictionary<K,V>();
  foreach(TSource item in source)
  {
    //overwrites previous values
    output[keySelector(item)] = valueSelector(item); 

    //ignores future duplicates, comment above and 
    //uncomment below to change behavior
    //K key = keySelector(item);
    //if(!output.ContainsKey(key))
    //{
      //output.Add(key, valueSelector(item));
    //}
  }

  return output;
}

我认为你可以弄清楚如何实施额外的重载(不含值的选择)。



Answer 3:

您可以使用Tuple来通过多个密钥。 检查下面的示例代码:

.ToDictionary(k => new Tuple<string,string>(k.key1,k.key2), v => v.value)


文章来源: C# Linq .ToDictionary() Key Already Exists