Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.1k views
in Technique[技术] by (71.8m points)

regex - C# Linq .ToDictionary() Key Already Exists

Final Edit: I was able to locate the duplicate field in the ini file. Thanks for your help everyone!

I'm using a regular expression to parse an ini file and LINQ to store it in a Dictionary:

Sample Data:
[WindowSettings]
Window X Pos='0'
Window Y Pos='0'
Window Maximized='false'
Window Name='Jabberwocky'

[Logging]
Directory='C:Rosetta StoneLogs'

EDIT: Here is the file actually causing the problem: http://pastebin.com/mQSrkrcP

EDIT2: I've narrowed it down to being caused by the last section in the file: [list_first_nonprintable]

For some reason one of the files that I'm parsing with this is throwing this exception:

System.ArgumentException: An item with the same key has already been added.

Is there any way for me to either find out which key is causing the problem (so I can fix the file), or to just skip the key that's causing this and continue parsing?

Here is the code:

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

    // Create regular expression to parse ini file.
    string pattern = @"^((?:[)(?<Section>[^]]*)(?:])(?:[
]{0,}|))((?![)(?<Key>[^=]*?)(?:=)(?<Value>[^
]*)(?:[
]{0,4}))*";
    //pattern = @"
    //^                           # Beginning of the line
    //((?:[)                     # Section Start
    //     (?<Section>[^]]*)     # Actual Section text into Section Group
    // (?:])                     # Section End then EOL/EOB
    // (?:[
]{0,}|))         # 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>[^
]*)        # Get everything that is not an Line Changes
    //  (?:[
]{0,4})           # MBDC 

    //  )*                        # 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>>();
}

Thanks in advance.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

This just means that when you convert to a Dictionary --

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

-- there are multiple keys (itm.Section). You can use ToLookup instead, which is kind of like a dictionary but allows multiple keys.

Edit

There are a couple of ways to call ToLookup. The simplest is to specify the key selector:

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

This should provide a lookup where the key is of type Group. Getting a lookup value should then return an IEnumerable, where T is the anonymous type:

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

If the .NET compiler doesn't like this (sometimes it seems to have trouble figuring out what the various types should be), you can also specify an element selector, for example:

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...