Lesen / Schreiben einer INI-Datei

263

Gibt es eine Klasse im .NET Framework, die Standard-INI-Dateien lesen / schreiben kann:

[Section]
<keyname>=<value>
...

Delphi hat die TIniFileKomponente und ich möchte wissen, ob es etwas Ähnliches für C # gibt?

Zendar
quelle
RemObjects verfügt über eine Delphi Prism-Bibliothek namens ShineOn, die eine ähnliche INI-Dateiklasse enthält. Sie benötigen jedoch Delphi Prism, um es aus dem Quellcode für .NET zu kompilieren, da noch keine kompilierte Assembly verfügbar ist. code.remobjects.com/p/shineon
Lex Li
1
Habe das gleiche Problem und habe meine eigene Bibliothek zum Parsen von INI-Dateien erstellt: github.com/rickyah/ini-parser Hoffe, es hilft
Ricardo Amores
5
Genau wie Ricky habe ich beschlossen, meine eigene Lösung dafür zu finden. Es ist verfügbar unter: github.com/MarioZ/MadMilkman.Ini
Mario Z

Antworten:

185

Die Ersteller des .NET Frameworks möchten, dass Sie XML-basierte Konfigurationsdateien anstelle von INI-Dateien verwenden. Also nein, es gibt keinen eingebauten Mechanismus zum Lesen.

Es sind jedoch Lösungen von Drittanbietern verfügbar.

David Arno
quelle
24
Obwohl es stimmt, dass XML-Konfigurationsdateien der richtige Weg sind, ist dies immer noch keine Antwort auf die Frage oder VLQ nur für Links.
Danny Beckett
6
@aloneguid Ich würde argumentieren, dass die große Anzahl verfügbarer Funktionen tatsächlich dazu beigetragen hat, dass .NET-Konfigurationsdateien seltsame Giganten mit viel Magie sind. Sie sind zu "Code in der Konfigurationsdatei" geworden, was zu viel Komplexität und seltsamen Verhaltensweisen führt und das Konfigurationsmanagement erschwert. (Ich sehe Sie, Datenbank- "Anbieter" und Verbindungszeichenfolgen.) Daher sind INI-Dateien im Allgemeinen auch besser für die nicht manuelle Bearbeitung geeignet.
jpmc26
1
Ich mag alte Methode (P / Inovke) und Sie können Unicode mit alten Methode wie folgt verwenden: File.WriteAllBytes (Pfad, neues Byte [] {0xFF, 0xFE});
Segelfisch009
2
Gutes Paket, aber es könnte besser sein. Es kann keinen Wert analysieren, der '=' oder '\ n' vollständig enthält
Ahmad Behjati
211

Vorwort

Lesen Sie zunächst diesen MSDN-Blogbeitrag über die Einschränkungen von INI-Dateien . Wenn es Ihren Bedürfnissen entspricht, lesen Sie weiter.

Dies ist eine prägnante Implementierung, die ich unter Verwendung des ursprünglichen Windows P / Invoke geschrieben habe. Sie wird daher von allen Windows-Versionen mit installiertem .NET (dh Windows 98 - Windows 10) unterstützt. Ich gebe es hiermit öffentlich zugänglich - Sie können es ohne Namensnennung kommerziell nutzen.

Die winzige Klasse

Fügen Sie IniFile.csIhrem Projekt eine neue Klasse hinzu :

using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;

// Change this to match your program's normal namespace
namespace MyProg
{
    class IniFile   // revision 11
    {
        string Path;
        string EXE = Assembly.GetExecutingAssembly().GetName().Name;

        [DllImport("kernel32", CharSet = CharSet.Unicode)]
        static extern long WritePrivateProfileString(string Section, string Key, string Value, string FilePath);

        [DllImport("kernel32", CharSet = CharSet.Unicode)]
        static extern int GetPrivateProfileString(string Section, string Key, string Default, StringBuilder RetVal, int Size, string FilePath);

        public IniFile(string IniPath = null)
        {
            Path = new FileInfo(IniPath ?? EXE + ".ini").FullName;
        }

        public string Read(string Key, string Section = null)
        {
            var RetVal = new StringBuilder(255);
            GetPrivateProfileString(Section ?? EXE, Key, "", RetVal, 255, Path);
            return RetVal.ToString();
        }

        public void Write(string Key, string Value, string Section = null)
        {
            WritePrivateProfileString(Section ?? EXE, Key, Value, Path);
        }

        public void DeleteKey(string Key, string Section = null)
        {
            Write(Key, null, Section ?? EXE);
        }

        public void DeleteSection(string Section = null)
        {
            Write(null, null, Section ?? EXE);
        }

        public bool KeyExists(string Key, string Section = null)
        {
            return Read(Key, Section).Length > 0;
        }
    }
}

Wie man es benutzt

Öffnen Sie die INI-Datei auf eine der folgenden drei Arten:

// Creates or loads an INI file in the same directory as your executable
// named EXE.ini (where EXE is the name of your executable)
var MyIni = new IniFile();

// Or specify a specific name in the current dir
var MyIni = new IniFile("Settings.ini");

// Or specify a specific name in a specific dir
var MyIni = new IniFile(@"C:\Settings.ini");

Sie können einige Werte wie folgt schreiben:

MyIni.Write("DefaultVolume", "100");
MyIni.Write("HomePage", "http://www.google.com");

So erstellen Sie eine Datei wie folgt:

[MyProg]
DefaultVolume=100
HomePage=http://www.google.com

So lesen Sie die Werte aus der INI-Datei aus:

var DefaultVolume = IniFile.Read("DefaultVolume");
var HomePage = IniFile.Read("HomePage");

Optional können Sie Folgendes einstellen [Section]:

MyIni.Write("DefaultVolume", "100", "Audio");
MyIni.Write("HomePage", "http://www.google.com", "Web");

So erstellen Sie eine Datei wie folgt:

[Audio]
DefaultVolume=100

[Web]
HomePage=http://www.google.com

Sie können auch prüfen, ob ein Schlüssel wie folgt vorhanden ist:

if(!MyIni.KeyExists("DefaultVolume", "Audio"))
{
    MyIni.Write("DefaultVolume", "100", "Audio");
}

Sie können einen Schlüssel wie folgt löschen:

MyIni.DeleteKey("DefaultVolume", "Audio");

Sie können auch einen ganzen Abschnitt (einschließlich aller Schlüssel) wie folgt löschen:

MyIni.DeleteSection("Web");

Bitte zögern Sie nicht, mit Verbesserungen zu kommentieren!

Danny Beckett
quelle
4
Ich bin etwas spät dran, aber es fehlt die GetSections()Methode.
Stil
2
Möglicherweise wäre eine traditionellere Standardeinstellung pro Anwendung (nicht pro Baugruppe) INI-Dateien wie Path.GetFullPath(IniPath ?? Path.ChangeExtension(Application.ExecutablePath, ".ini")).
Eugene
3
Echt super ! Auf Github legen?
Emrys Myrooin
2
@danny Beckett, schön gemacht. Dies entspricht fast genau dem, was ich in den letzten Jahren von .Net verwendet habe. Vor Jahren von altem Code aktualisiert.
Damian
10
Alt und so sehr ich Raymond Chen respektiere, waren viele der Einschränkungen in diesem Artikel Einschränkungen der spezifischen INI-Bibliothek in Windows und nicht des INI-Formats selbst. Andere, wie granulare Berechtigungen, können über mehrere Dateien leicht umgangen werden. Eine offizielle , modernisierte INI-Bibliothek wäre auch heute noch sehr willkommen.
Joel Coehoorn
68

Dieser Artikel über CodeProject " Eine INI- Dateibehandlungsklasse mit C # " sollte helfen.

Der Autor hat eine C # -Klasse "Ini" erstellt, die zwei Funktionen aus KERNEL32.dll verfügbar macht. Diese Funktionen sind: WritePrivateProfileStringund GetPrivateProfileString. Sie benötigen zwei Namespaces: System.Runtime.InteropServicesund System.Text.

Schritte zur Verwendung der Ini-Klasse

Fügen Sie in Ihrer Projekt-Namespace-Definition hinzu

using INI;

Erstellen Sie eine INIFile wie diese

INIFile ini = new INIFile("C:\\test.ini");

Verwenden Sie IniWriteValuediese Option , um einen neuen Wert in einen bestimmten Schlüssel in einem Abschnitt zu schreiben oder um IniReadValueeinen Wert aus einem Schlüssel in einem bestimmten Abschnitt zu lesen.

Hinweis: Wenn Sie bei Null anfangen, können Sie diesen MSDN-Artikel lesen : Gewusst wie: Hinzufügen von Anwendungskonfigurationsdateien zu C # -Projekten . Dies ist eine bessere Möglichkeit, Ihre Anwendung zu konfigurieren.

splattne
quelle
1
Ich möchte die komplette INI-Datei lesen. Wie man das gleiche macht, anstatt Abschnitt zu lesen, key
venkat
Das funktionierte für mich und hörte dann von einem anderen Punkt aus auf zu arbeiten. Keine Ahnung noch, was unter der Haube anders lief
nawfal
1
Achten Sie darauf, diese veralteten Win32-API-Funktionen zu verwenden. Weitere Informationen: stackoverflow.com/questions/11451641/…
Pedro77
Ich habe diesen Ansatz für eine Weile verwendet, aber Sicherheitsverbesserungen ab Win7 haben dies für mich so gut wie zunichte gemacht. Sie können diesen Ansatz weiterhin verwenden, aber Sie müssen die INI in ProgramData speichern und Ihre App dort lesen / schreiben lassen.
Jess
Speichern Sie keine Anwendungskonfigurations-INI-Dateien in ProgramData. Sie gehören weder in die Registrierung noch in die Programmdaten. Konfigurationsdateien sollten sich in den LocalApplicationData-Ordnern befinden.
Deegee
47

Ich fand diese einfache Implementierung:

http://bytes.com/topic/net/insights/797169-reading-parsing-ini-file-c

Funktioniert gut für das, was ich brauche.

So verwenden Sie es:

public class TestParser
{
    public static void Main()
    {
        IniParser parser = new IniParser(@"C:\test.ini");

        String newMessage;

        newMessage = parser.GetSetting("appsettings", "msgpart1");
        newMessage += parser.GetSetting("appsettings", "msgpart2");
        newMessage += parser.GetSetting("punctuation", "ex");

        //Returns "Hello World!"
        Console.WriteLine(newMessage);
        Console.ReadLine();
    }
}

Hier ist der Code:

using System;
using System.IO;
using System.Collections;

public class IniParser
{
    private Hashtable keyPairs = new Hashtable();
    private String iniFilePath;

    private struct SectionPair
    {
        public String Section;
        public String Key;
    }

    /// <summary>
    /// Opens the INI file at the given path and enumerates the values in the IniParser.
    /// </summary>
    /// <param name="iniPath">Full path to INI file.</param>
    public IniParser(String iniPath)
    {
        TextReader iniFile = null;
        String strLine = null;
        String currentRoot = null;
        String[] keyPair = null;

        iniFilePath = iniPath;

        if (File.Exists(iniPath))
        {
            try
            {
                iniFile = new StreamReader(iniPath);

                strLine = iniFile.ReadLine();

                while (strLine != null)
                {
                    strLine = strLine.Trim().ToUpper();

                    if (strLine != "")
                    {
                        if (strLine.StartsWith("[") && strLine.EndsWith("]"))
                        {
                            currentRoot = strLine.Substring(1, strLine.Length - 2);
                        }
                        else
                        {
                            keyPair = strLine.Split(new char[] { '=' }, 2);

                            SectionPair sectionPair;
                            String value = null;

                            if (currentRoot == null)
                                currentRoot = "ROOT";

                            sectionPair.Section = currentRoot;
                            sectionPair.Key = keyPair[0];

                            if (keyPair.Length > 1)
                                value = keyPair[1];

                            keyPairs.Add(sectionPair, value);
                        }
                    }

                    strLine = iniFile.ReadLine();
                }

            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                if (iniFile != null)
                    iniFile.Close();
            }
        }
        else
            throw new FileNotFoundException("Unable to locate " + iniPath);

    }

    /// <summary>
    /// Returns the value for the given section, key pair.
    /// </summary>
    /// <param name="sectionName">Section name.</param>
    /// <param name="settingName">Key name.</param>
    public String GetSetting(String sectionName, String settingName)
    {
        SectionPair sectionPair;
        sectionPair.Section = sectionName.ToUpper();
        sectionPair.Key = settingName.ToUpper();

        return (String)keyPairs[sectionPair];
    }

    /// <summary>
    /// Enumerates all lines for given section.
    /// </summary>
    /// <param name="sectionName">Section to enum.</param>
    public String[] EnumSection(String sectionName)
    {
        ArrayList tmpArray = new ArrayList();

        foreach (SectionPair pair in keyPairs.Keys)
        {
            if (pair.Section == sectionName.ToUpper())
                tmpArray.Add(pair.Key);
        }

        return (String[])tmpArray.ToArray(typeof(String));
    }

    /// <summary>
    /// Adds or replaces a setting to the table to be saved.
    /// </summary>
    /// <param name="sectionName">Section to add under.</param>
    /// <param name="settingName">Key name to add.</param>
    /// <param name="settingValue">Value of key.</param>
    public void AddSetting(String sectionName, String settingName, String settingValue)
    {
        SectionPair sectionPair;
        sectionPair.Section = sectionName.ToUpper();
        sectionPair.Key = settingName.ToUpper();

        if (keyPairs.ContainsKey(sectionPair))
            keyPairs.Remove(sectionPair);

        keyPairs.Add(sectionPair, settingValue);
    }

    /// <summary>
    /// Adds or replaces a setting to the table to be saved with a null value.
    /// </summary>
    /// <param name="sectionName">Section to add under.</param>
    /// <param name="settingName">Key name to add.</param>
    public void AddSetting(String sectionName, String settingName)
    {
        AddSetting(sectionName, settingName, null);
    }

    /// <summary>
    /// Remove a setting.
    /// </summary>
    /// <param name="sectionName">Section to add under.</param>
    /// <param name="settingName">Key name to add.</param>
    public void DeleteSetting(String sectionName, String settingName)
    {
        SectionPair sectionPair;
        sectionPair.Section = sectionName.ToUpper();
        sectionPair.Key = settingName.ToUpper();

        if (keyPairs.ContainsKey(sectionPair))
            keyPairs.Remove(sectionPair);
    }

    /// <summary>
    /// Save settings to new file.
    /// </summary>
    /// <param name="newFilePath">New file path.</param>
    public void SaveSettings(String newFilePath)
    {
        ArrayList sections = new ArrayList();
        String tmpValue = "";
        String strToSave = "";

        foreach (SectionPair sectionPair in keyPairs.Keys)
        {
            if (!sections.Contains(sectionPair.Section))
                sections.Add(sectionPair.Section);
        }

        foreach (String section in sections)
        {
            strToSave += ("[" + section + "]\r\n");

            foreach (SectionPair sectionPair in keyPairs.Keys)
            {
                if (sectionPair.Section == section)
                {
                    tmpValue = (String)keyPairs[sectionPair];

                    if (tmpValue != null)
                        tmpValue = "=" + tmpValue;

                    strToSave += (sectionPair.Key + tmpValue + "\r\n");
                }
            }

            strToSave += "\r\n";
        }

        try
        {
            TextWriter tw = new StreamWriter(newFilePath);
            tw.Write(strToSave);
            tw.Close();
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    /// <summary>
    /// Save settings back to ini file.
    /// </summary>
    public void SaveSettings()
    {
        SaveSettings(iniFilePath);
    }
}
Joerage
quelle
38
+1, um über dem Downvote auszugleichen. Worüber beschweren Sie sich wirklich? Er sagte, er habe es gefunden. Stimmen Sie ihn ab, weil er keinen mit generischen Accessoren und Stringbuilder-Verwendung gefunden hat?
Tormod
1
@Tormod: Ich wünschte, ich könnte den Kommentar ablehnen. Es ist ein technisches Forum, wenn wir über Lösungen abstimmen, nicht über die (offensichtlich positive) Absicht. Wenn eine von Knuth selbst veröffentlichte Lösung Mängel hätte, würde - und sollte - darauf hingewiesen werden. Es spielt keine Rolle, ob die Lösung vom Poster gefunden oder geschrieben wurde.
ya23
7
Ich denke, Sie strecken die Definition von "Fehler". Wenn die Lösung Ihre Empfindlichkeiten nicht betont, stimmen Sie einfach nicht ab. Ich habe gerade eine Nachricht hinterlassen, dass ich seine Ablehnung bereits negiert habe, damit die anderen 7 Leute, die meinen Kommentar positiv bewertet haben, dies nicht selbst tun.
Tormod
21

Der Code in Joerages Antwort ist inspirierend.

Leider ändert es die Groß- und Kleinschreibung der Tasten und behandelt keine Kommentare. Also habe ich etwas geschrieben, das robust genug sein sollte, um (nur) sehr schmutzige INI-Dateien zu lesen und Schlüssel so abzurufen, wie sie sind.

Es verwendet etwas LINQ, ein verschachteltes String-Wörterbuch ohne Berücksichtigung der Groß- und Kleinschreibung, um Abschnitte, Schlüssel und Werte zu speichern und die Datei auf einmal zu lesen.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

class IniReader
{
    Dictionary<string, Dictionary<string, string>> ini = new Dictionary<string, Dictionary<string, string>>(StringComparer.InvariantCultureIgnoreCase);

    public IniReader(string file)
    {
        var txt = File.ReadAllText(file);

        Dictionary<string, string> currentSection = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);

        ini[""] = currentSection;

        foreach(var line in txt.Split(new[]{"\n"}, StringSplitOptions.RemoveEmptyEntries)
                               .Where(t => !string.IsNullOrWhiteSpace(t))
                               .Select(t => t.Trim()))
        {
            if (line.StartsWith(";"))
                continue;

            if (line.StartsWith("[") && line.EndsWith("]"))
            {
                currentSection = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
                ini[line.Substring(1, line.LastIndexOf("]") - 1)] = currentSection;
                continue;
            }

            var idx = line.IndexOf("=");
            if (idx == -1)
                currentSection[line] = "";
            else
                currentSection[line.Substring(0, idx)] = line.Substring(idx + 1);
        }
    }

    public string GetValue(string key)
    {
        return GetValue(key, "", "");
    }

    public string GetValue(string key, string section)
    {
        return GetValue(key, section, "");
    }

    public string GetValue(string key, string section, string @default)
    {
        if (!ini.ContainsKey(section))
            return @default;

        if (!ini[section].ContainsKey(key))
            return @default;

        return ini[section][key];
    }

    public string[] GetKeys(string section)
    {
        if (!ini.ContainsKey(section))
            return new string[0];

        return ini[section].Keys.ToArray();
    }

    public string[] GetSections()
    {
        return ini.Keys.Where(t => t != "").ToArray();
    }
}
Larry
quelle
4
und danke für nicht setzen , dass catch (Exception ex) { throw ex; }dort
Mark Schultheiss
1
Gut! Zumindest einige Änderungen sind erforderlich, um besser zu funktionieren. Zeile 16: ini [""] = currentSection; To: // ini [""] = currentSection; Dies muss entfernt werden, da das erste Element [0] aufgrund dieser Initialisierung jedes Mal ein leeres Segment ist. Zeile 36: currentSection [line.Substring (0, idx)] = line.Substring (idx + 1); An: currentSection [line.Substring (0, idx) .Trim ()] = line.Substring (idx + 1) .Trim (); Schlüssel und Werte sollten unabhängig voneinander zugeschnitten werden, nicht nur in der Zeile Trimmen. In INI-ähnlichen Konfigurationsdateien neigen normalerweise diejenigen, die K-> V-Paare hinzufügen, dazu, diese Gleichheit innerhalb von Abschnitten auszurichten. Danke dir!
LXSoft
Es war lange her. Vielen Dank für Ihre Vorschläge. Sie alle machen Sinn und verdienen diesen Code für eine gute Aktualisierung.
Larry
13

Ich möchte eine IniParser-Bibliothek einführen, die ich vollständig in c # erstellt habe, damit sie in keinem Betriebssystem Abhängigkeiten enthält, wodurch sie Mono-kompatibel ist. Open Source mit MIT-Lizenz - kann also in jedem Code verwendet werden.

Sie können die Quelle in GitHub überprüfen und sie ist auch als NuGet-Paket verfügbar

Es ist stark konfigurierbar und sehr einfach zu bedienen .

Entschuldigen Sie den schamlosen Stecker, aber ich hoffe, er kann jedem helfen, der diese Antwort noch einmal überprüft.

Ricardo Amores
quelle
4

Wenn Sie nur Lese- und keinen Schreibzugriff benötigen und das verwenden Microsoft.Extensions.Confiuration(wird standardmäßig mit ASP.NET Core geliefert, funktioniert aber auch mit regulären Programmen), können Sie mit dem NuGet-Paket Microsoft.Extensions.Configuration.IniINI-Dateien in Ihre Konfigurationseinstellungen importieren.

public Startup(IHostingEnvironment env)
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddIniFile("SomeConfig.ini", optional: false);
    Configuration = builder.Build();
}
Scott Chamberlain
quelle
Nur um hinzuzufügen, dass Sie dann Schlüssel mitConfiguration["keyname"]
Kofifus
@scott Das Problem ist, dass IIS es aus irgendeinem Grund nicht erkennt, wenn die App ausgeführt wird. Es wird dort bereitgestellt, aber nicht verbraucht. HTTP 500.30 wird zurückgegeben und im IIS-App-Protokoll wird angezeigt, dass die Konfigurationsdatei nicht gefunden wurde und nicht optional ist.
one.beat.consumer
3

Wenn Sie Anwendungen mit C # und dem .NET Framework erstellen, werden normalerweise keine INI-Dateien verwendet. Es ist üblicher, Einstellungen in einer XML-basierten Konfigurationsdatei oder in der Registrierung zu speichern. Wenn Ihre Software jedoch Einstellungen mit einer Legacy-Anwendung teilt, ist es möglicherweise einfacher, die Konfigurationsdatei zu verwenden, als die Informationen an anderer Stelle zu duplizieren.

Das .NET Framework unterstützt die direkte Verwendung von INI-Dateien nicht. Sie können jedoch Windows-API-Funktionen mit Platform Invocation Services (P / Invoke) verwenden, um in die Dateien zu schreiben und aus ihnen zu lesen. In diesem Link erstellen wir eine Klasse, die INI-Dateien darstellt und diese mithilfe von Windows-API-Funktionen bearbeitet. Bitte gehen Sie über den folgenden Link.

Lesen und Schreiben von INI-Dateien

Unbekannt
quelle
4
Bleib aus der Registrierung heraus! Anwendungskonfigurationsdaten sollten nicht in der Registrierung gespeichert werden.
Deegee
3

Wenn Sie nur einen einfachen Reader ohne Abschnitte und andere DLLs möchten, ist dies eine einfache Lösung:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Tool
{
    public class Config
    {
        Dictionary <string, string> values;
        public Config (string path)
        {
            values = File.ReadLines(path)
            .Where(line => (!String.IsNullOrWhiteSpace(line) && !line.StartsWith("#")))
            .Select(line => line.Split(new char[] { '=' }, 2, 0))
            .ToDictionary(parts => parts[0].Trim(), parts => parts.Length>1?parts[1].Trim():null);
        }
        public string Value (string name, string value=null)
        {
            if (values!=null && values.ContainsKey(name))
            {
                return values[name];
            }
            return value;
        }
    }
}

Anwendungsbeispiel:

    file = new Tool.Config (Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\config.ini");
    command = file.Value ("command");
    action = file.Value ("action");
    string value;
    //second parameter is default value if no key found with this name
    value = file.Value("debug","true");
    this.debug = (value.ToLower()=="true" || value== "1");
    value = file.Value("plain", "false");
    this.plain = (value.ToLower() == "true" || value == "1");

Konfigurieren Sie den Inhalt der Datei währenddessen (wie Sie sehen, unterstützt das Symbol # für den Zeilenkommentar):

#command to run
command = php

#default script
action = index.php

#debug mode
#debug = true

#plain text mode
#plain = false

#icon = favico.ico
BIOHAZARD
quelle
3

Versuchen Sie diese Methode:

public static Dictionary<string, string> ParseIniDataWithSections(string[] iniData)
{
    var dict = new Dictionary<string, string>();
    var rows = iniData.Where(t => 
        !String.IsNullOrEmpty(t.Trim()) && !t.StartsWith(";") && (t.Contains('[') || t.Contains('=')));
    if (rows == null || rows.Count() == 0) return dict;
    string section = "";
    foreach (string row in rows)
    {
        string rw = row.TrimStart();
        if (rw.StartsWith("["))
            section = rw.TrimStart('[').TrimEnd(']');
        else
        {
            int index = rw.IndexOf('=');
            dict[section + "-" + rw.Substring(0, index).Trim()] = rw.Substring(index+1).Trim().Trim('"');
        }
    }
    return dict;
}

Es wird das Wörterbuch erstellt, in dem der Schlüssel "-" lautet. Sie können es folgendermaßen laden:

var dict = ParseIniDataWithSections(File.ReadAllLines(fileName));
Petr Voborník
quelle
3

PeanutButter.INI ist eine Nuget-Paketklasse für die Manipulation von INI-Dateien. Es unterstützt Lesen / Schreiben, einschließlich Kommentare - Ihre Kommentare bleiben beim Schreiben erhalten. Es scheint ziemlich beliebt zu sein, ist getestet und einfach zu bedienen. Es ist auch völlig kostenlos und Open Source.

Haftungsausschluss: Ich bin der Autor von PeanutButter.INI.

daf
quelle
Könnten Sie bitte einen Link zur PeanutButter.INI-Dokumentation bereitstellen?
Shroombaker
3

Ich bin spät dran, um an der Party teilzunehmen, aber ich hatte heute das gleiche Problem und habe die folgende Implementierung geschrieben:

using System.Text.RegularExpressions;

static bool match(this string str, string pat, out Match m) =>
    (m = Regex.Match(str, pat, RegexOptions.IgnoreCase)).Success;

static void Main()
{
    Dictionary<string, Dictionary<string, string>> ini = new Dictionary<string, Dictionary<string, string>>();
    string section = "";

    foreach (string line in File.ReadAllLines(.........)) // read from file
    {
        string ln = (line.Contains('#') ? line.Remove(line.IndexOf('#')) : line).Trim();

        if (ln.match(@"^[ \t]*\[(?<sec>[\w\-]+)\]", out Match m))
            section = m.Groups["sec"].ToString();
        else if (ln.match(@"^[ \t]*(?<prop>[\w\-]+)\=(?<val>.*)", out m))
        {
            if (!ini.ContainsKey(section))
                ini[section] = new Dictionary<string, string>();

            ini[section][m.Groups["prop"].ToString()] = m.Groups["val"].ToString();
        }
    }


    // access the ini file as follows:
    string content = ini["section"]["property"];
}

Es ist zu beachten, dass diese Implementierung keine Abschnitte oder Eigenschaften verarbeitet, die nicht gefunden wurden. Um dies zu erreichen, sollten Sie die Dictionary<,>Klasse erweitern, um nicht gefundene Schlüssel zu verarbeiten.


Um eine Instanz von Dictionary<string, Dictionary<string, string>>in eine .ini-file zu serialisieren , verwende ich den folgenden Code:

string targetpath = .........;
Dictionary<string, Dictionary<string, string>> ini = ........;
StringBuilder sb = new StringBuilder();

foreach (string section in ini.Keys)
{
    sb.AppendLine($"[{section}]");

    foreach (string property in ini[section].Keys)
        sb.AppendLine($"{property}={ini[section][property]");
}

File.WriteAllText(targetpath, sb.ToString());
unbekannt6656
quelle
2

In CommonLibrary.NET ist ein Ini-Parser verfügbar

Dies hat verschiedene sehr bequeme Überlastungen zum Abrufen von Abschnitten / Werten und ist sehr leicht.

James
quelle
1
Für den Fall, dass es nicht offensichtlich ist, die oberste Ebene der Bibliothek zu betrachten (es war mir nicht klar!), Sind die IniDcoument-Klasse und andere in ComLib.IO.
Tim Keating
2
Für alle, die sich diese Route ansehen, scheint CommonLibrary.NET nicht den INI-Konventionen zu folgen. Es wird ein Doppelpunkt ":" als Trennzeichen anstelle des Gleichheitszeichens verwendet und es werden keine Kommentare verarbeitet (das Beginnen einer Zeile mit einem Semikolon oder einem Nummernzeichen führt dazu, dass die Analyse fehlschlägt).
jmmr
2

Hier ist meine eigene Version mit regulären Ausdrücken. In diesem Code wird davon ausgegangen, dass jeder Abschnittsname eindeutig ist. Wenn dies jedoch nicht der Fall ist, ist es sinnvoll, Dictionary durch List zu ersetzen. Diese Funktion unterstützt das Kommentieren von INI-Dateien ab ';' Charakter. Abschnitt beginnt normal [Abschnitt], und Schlüsselwertpaare kommen normalerweise auch "Schlüssel = Wert". Gleiche Annahme wie für Abschnitte - Schlüsselname ist eindeutig.

/// <summary>
/// Loads .ini file into dictionary.
/// </summary>
public static Dictionary<String, Dictionary<String, String>> loadIni(String file)
{
    Dictionary<String, Dictionary<String, String>> d = new Dictionary<string, Dictionary<string, string>>();

    String ini = File.ReadAllText(file);

    // Remove comments, preserve linefeeds, if end-user needs to count line number.
    ini = Regex.Replace(ini, @"^\s*;.*$", "", RegexOptions.Multiline);

    // Pick up all lines from first section to another section
    foreach (Match m in Regex.Matches(ini, "(^|[\r\n])\\[([^\r\n]*)\\][\r\n]+(.*?)(\\[([^\r\n]*)\\][\r\n]+|$)", RegexOptions.Singleline))
    {
        String sectionName = m.Groups[2].Value;
        Dictionary<String, String> lines = new Dictionary<String, String>();

        // Pick up "key = value" kind of syntax.
        foreach (Match l in Regex.Matches(ini, @"^\s*(.*?)\s*=\s*(.*?)\s*$", RegexOptions.Multiline))
        {
            String key = l.Groups[1].Value;
            String value = l.Groups[2].Value;

            // Open up quotation if any.
            value = Regex.Replace(value, "^\"(.*)\"$", "$1");

            if (!lines.ContainsKey(key))
                lines[key] = value;
        }

        if (!d.ContainsKey(sectionName))
            d[sectionName] = lines;
    }

    return d;
}
TarmoPikaro
quelle
Diese Funktion funktioniert für mich nicht: Sie vergisst jeden zweiten Abschnitt. Ich habe es mit und ohne Leerzeilen vor [Abschnitt] versucht.
Iksess
Können Sie ein Beispiel Ihrer .ini kopieren, das nicht funktioniert?
TarmoPikaro
-3

Hier ist meine Klasse, funktioniert wie ein Zauber:

public static class IniFileManager
{


    [DllImport("kernel32")]
    private static extern long WritePrivateProfileString(string section,
        string key, string val, string filePath);
    [DllImport("kernel32")]
    private static extern int GetPrivateProfileString(string section,
             string key, string def, StringBuilder retVal,
        int size, string filePath);
    [DllImport("kernel32.dll")]
    private static extern int GetPrivateProfileSection(string lpAppName,
             byte[] lpszReturnBuffer, int nSize, string lpFileName);


    /// <summary>
    /// Write Data to the INI File
    /// </summary>
    /// <PARAM name="Section"></PARAM>
    /// Section name
    /// <PARAM name="Key"></PARAM>
    /// Key Name
    /// <PARAM name="Value"></PARAM>
    /// Value Name
    public static void IniWriteValue(string sPath,string Section, string Key, string Value)
    {
        WritePrivateProfileString(Section, Key, Value, sPath);
    }

    /// <summary>
    /// Read Data Value From the Ini File
    /// </summary>
    /// <PARAM name="Section"></PARAM>
    /// <PARAM name="Key"></PARAM>
    /// <PARAM name="Path"></PARAM>
    /// <returns></returns>
    public static string IniReadValue(string sPath,string Section, string Key)
    {
        StringBuilder temp = new StringBuilder(255);
        int i = GetPrivateProfileString(Section, Key, "", temp,
                                        255, sPath);
        return temp.ToString();

    }

}}

Die Verwendung ist offensichtlich, da es sich um eine statische Klasse handelt. Rufen Sie einfach IniFileManager.IniWriteValue zum Lesen eines Abschnitts oder IniFileManager.IniReadValue zum Lesen eines Abschnitts auf.

Erwin Draconis
quelle
Dieser Ansatz wurde bereits in einer anderen Antwort gezeigt und erklärt . Was fügt Ihre Antwort hinzu, die von dieser nicht abgedeckt wird?
Palec
Beachten Sie, dass dies nur funktioniert, wenn die INI-Datei in UNICODE (16-Bit-LE) gespeichert ist. Verwenden Sie Notepad ++, um den Text in Unicode zu konvertieren, denn wenn Sie ihn in UTF-8 speichern, funktioniert er nicht. Auch ANSI ist akzeptabel, aber Sie können keine Buchstaben mit Akzent lesen
user2991288
-6

Sie sollten Daten aus XML-Dateien lesen und schreiben, da Sie ein ganzes Objekt in XML speichern und ein Objekt aus einer gespeicherten XML-Datei füllen können. Es ist besser, Objekte einfach zu manipulieren.

So geht's: Objektdaten in eine XML-Datei schreiben: https://msdn.microsoft.com/en-us/library/ms172873.aspx Objektdaten aus einer XML-Datei lesen: https://msdn.microsoft. com / de-de / library / ms172872.aspx

Daniel
quelle
1
Links zu externen Ressourcen werden empfohlen. Fügen Sie jedoch einen Kontext um den Link hinzu, damit Ihre Mitbenutzer eine Vorstellung davon haben, was es ist und warum es dort ist. Zitieren Sie immer den relevantesten Teil eines wichtigen Links, falls die Zielwebsite nicht erreichbar ist oder dauerhaft offline geht.
Davejal
Ich glaube, dass die Links Titel sehr klar über seine Referenzen / Kontext sind. Wenn Sie der Meinung sind, dass dies nicht ausreicht, können Sie es jederzeit bearbeiten.
Daniel
1
Behandelt nicht die eigentliche Frage.
Erik Knowles