2

I just installed ini-parser 2.5.2 (for .NET 4.7 application) and ran into an issue testing it.

My .ini file has entries like this:

[INSTANCES]
NUMVTC=3
VTC=0,3330,ABCD,Descr1
VTC=1,3331,EFGH,Descr2
VTC=2,3332,IJKL,Descr3

To test it, I attempted to parse above section using:

List<string> ls = GetIniSectionData("path-to-some-ini", "instances", "VTC");

Piece of code where exception is thrown:

public static List<string> GetIniSectionData(string iniFilePath, string section, string key)
{
    List<string> ls = new List<string>();
    var parser = FileIniDataParser();
    IniData data = parser.ReadFile(iniFilePath);  // barfs here: Parsing exception: Duplicate key 'VTC' found in section 'INSTANCES'
    int cnt = data[section].Count;
    while(!string.IsNullOrEmpty(data[section][key]))
    {
        ls.add(data[section][key]);
    }
    return ls;
}

How do I get around duplicate key-value issue?

Update

After fiddling with ini-parser for some time and realizing it probably can't handle duplicate keys, I used the following using Linq. Not sure how robust it is, i guess I will find out soon!

var vals = File.ReadLines(sMappingFilePath)
    .SkipWhile(line => !line.StartsWith("[instances]", 
      StringComparison.CurrentCultureIgnoreCase))
    .Skip(1)
    .TakeWhile(line => !string.IsNullOrEmpty(line))
    .Select(line => new
    {
        Key = line.Split('=')[0],
        Value = line.Split('=')[1]
    });
foreach (var val in vals)
{
    string[] saValues = val.Value.Split(',');

    if (saValues.Length == 4)
    {
        int vtcNum = Convert.ToInt16(saValues[0]);
        int vtcPort = Convert.ToInt16(saValues[1]);
        string vtcName = saValues[2];
        string vtcDescr = saValues[3];
        ...
    }
}
14
  • 3
    Assuming it's github.com/rickyah/ini-parser There's a bunch of config for duplicate keys, did you try that? github.com/rickyah/ini-parser/wiki/Configuring-parser-behavior Commented Jun 10 at 13:10
  • 1
    If you configure to allow them, it will still take only the last value, though. Is that what you are expecting? Commented Jun 10 at 13:11
  • 1
    Oh if you are dealing with preexisting INIs, what did you use to parse them before? Commented Jun 10 at 13:53
  • 1
    Hmmm, he may have implemented that exact feature, which is uncommon for INI. Usually I wouldn't expect INI to support duplicate keys. Either throw an error or handle by overwriting, I would expect. "Multiple Values" are typically done by nesting. Commented Jun 10 at 14:00
  • 1
    That being said, if you cannot change the INIs themselves, you might have to stick with the old implementation or come up with your own. I wouldn't throw out the possibility another library supports that, but I really doubt it. Commented Jun 10 at 14:03

2 Answers 2

0

It isn't that difficult to write your own INI-parser:
(You may have to use a <LangVersion>latest</LangVersion> in your csproj file)

public record class IniValue(string? Section, string Key, string Value);

public static IEnumerable<IniValue> ReadIniFile(string path, string? section = null, string? key = null)
{
    string? currentSection = null;
    foreach (string line in File.ReadLines(path)) {
        string trimmedLine = line.Trim();
        if (trimmedLine is [] or [';', ..]) { // Skip empty lines and comments
            continue;
        }
        if (trimmedLine is ['[', .. string s, ']']) // Section header
        {
            currentSection = s;
        } else if (trimmedLine.Contains('=')) {// Key-value pair
            string[] parts = trimmedLine.Split(['='], 2); // Split only on the first '='
            string thisKey = parts[0].Trim();
            if ((section is null || String.Compare(section, currentSection, StringComparison.OrdinalIgnoreCase) == 0) &&
                (key is null || String.Compare(key, thisKey, StringComparison.OrdinalIgnoreCase) == 0)) {
                yield return new IniValue(currentSection, thisKey, parts[1].Trim());
            }
        }
    }
}

It can be tested like this

string path = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Example.INI");

Console.WriteLine("All sections and values");
foreach (IniValue value in ReadIniFile(path)) {
    Console.WriteLine(value);
}
Console.WriteLine();

Console.WriteLine("All values of [instances] section");
foreach (IniValue value in ReadIniFile(path, section: "instances")) {
    Console.WriteLine(value);
}
Console.WriteLine();

Console.WriteLine("Specific value in specific section");
foreach (IniValue value in ReadIniFile(path, section: "instances", key: "numvtc")) {
    Console.WriteLine(value);
}
Console.WriteLine();

Console.WriteLine("Repeated value in specific section");
foreach (IniValue value in ReadIniFile(path, section: "instances", key: "vtc")) {
    Console.WriteLine(value);
}
Console.WriteLine();

Console.WriteLine("Specific value in any section");
foreach (IniValue value in ReadIniFile(path, section: null, key: "numvtc")) {
    Console.WriteLine(value);
}
Console.WriteLine();

I used these INI-settings for the test:

[INSTANCES]
NUMVTC=3
VTC=0,3330,ABCD,Descr1
VTC=1,3331,EFGH,Descr2
VTC=2,3332,IJKL,Descr3

; This is a comment line
[Other Section]
numvtc=0
A=aaa=AAA
B = bbb
Sign up to request clarification or add additional context in comments.

4 Comments

"It isn't that difficult to write your own INI-parser" - INI files are so much more complex... Just one example: a section does not need to terminate in ]. At least when using the Microsoft API, which I take as the "official" reference, because it's probably the most used and very long around.
@ThomasWeller: You can always use GetPrivateProfielString if you want 100% compat, but does it return duplicate keys as the OP requests?
No, it doesn't. That's not a feature of Microsoft INI files. Yet, OP is using other features which look like the Microsoft implementation, e.g. having the section in the INI file in uppercase, but accessing it later with lowercase.
Yeah, my implementation is not case sensitive, just like the original, but it leaves the case as is in the return values.
-1

Looks like Ini-Parser supports this directly:

public static List<string> GetIniSectionData(string iniFilePath, string section, string key)
{
    var innerParser = new ConcatenateDuplicatedKeysIniDataParser();
    var parser = FileIniDataParser(innerParser);
    var data = parser.ReadFile(iniFilePath);
    return data[section][key]?.Split(innerParser.Configuration.ConcatenateSeparator).ToList();
}

It looks like the unpublished development branch of Ini-Parser has a different way of handling this configuration, but the fundamentals are the same. Just get the data, and split it on the separator.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.