web-dev-qa-db-fra.com

Lecture / écriture d'un fichier INI

Existe-t-il une classe dans le framework .NET capable de lire/écrire des fichiers .ini standard:

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

Delphi a le composant TIniFile et je veux savoir s’il existe quelque chose de similaire pour C #?

244
zendar

Les créateurs du framework .NET souhaitent que vous utilisiez des fichiers de configuration basés sur XML plutôt que des fichiers INI. Donc non, il n'y a pas de mécanisme intégré pour les lire.

Cependant, des solutions tierces sont disponibles.

173
David Arno

Préface

Tout d’abord, lisez ce billet de blog MSDN sur les limitations des fichiers INI . Si cela vous convient, lisez la suite.

C’est une implémentation concise que j’ai écrite, utilisant le P/Invoke de Windows d’origine, de sorte qu’elle est prise en charge par toutes les versions de Windows avec .NET installé (par exemple, Windows 98 - Windows 10). Je le libère par la présente dans le domaine public - vous êtes libre de l’utiliser commercialement sans attribution.

La petite classe

Ajoutez une nouvelle classe appelée IniFile.cs à votre projet:

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.ToString();
        }

        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;
        }
    }
}

Comment l'utiliser

Ouvrez le fichier INI de l'une des 3 manières suivantes:

// 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");

Vous pouvez écrire des valeurs comme ceci:

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

Pour créer un fichier comme celui-ci:

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

Pour lire les valeurs du fichier INI:

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

Vous pouvez éventuellement définir les paramètres de [Section]:

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

Pour créer un fichier comme celui-ci:

[Audio]
DefaultVolume=100

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

Vous pouvez également vérifier l'existence d'une clé comme ceci:

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

Vous pouvez supprimer une clé comme ceci:

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

Vous pouvez également supprimer une section entière (y compris toutes les clés) comme ceci:

MyIni.DeleteSection("Web");

S'il vous plaît n'hésitez pas à commenter avec des améliorations!

191
Danny Beckett

Cet article sur CodeProject " ne classe de traitement de fichier INI utilisant C # " devrait vous aider.

L'auteur a créé une classe C # "Ini" qui expose deux fonctions de KERNEL32.dll. Ces fonctions sont: WritePrivateProfileString et GetPrivateProfileString. Vous aurez besoin de deux espaces de noms: System.Runtime.InteropServices et System.Text.

Procédure pour utiliser la classe Ini

Dans la définition de votre espace de noms de projet, ajoutez

using INI;

Créer un INIFile comme celui-ci

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

Utilisez IniWriteValue pour écrire une nouvelle valeur sur une clé spécifique d’une section ou utilisez IniReadValue pour lire une valeur FROM une clé d’une section spécifique.

Remarque: si vous commencez à partir de zéro, vous pouvez lire cet article de MSDN : Comment: Ajouter Fichiers de configuration d’application aux projets C # . C'est un meilleur moyen de configurer votre application.

65
splattne

J'ai trouvé cette implémentation simple:

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

Fonctionne bien pour ce dont j'ai besoin.

Voici comment vous l'utilisez:

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

Voici le 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);
    }
}
44
joerage

Le code dans la réponse de joerage est inspirant.

Malheureusement, cela change la casse des touches et ne gère pas les commentaires. J'ai donc écrit quelque chose qui devrait être assez robuste pour lire (uniquement) les fichiers très sales INI et permet de récupérer les clés telles quelles.

Il utilise LINQ, un dictionnaire de chaînes imbriqué, insensible à la casse, pour stocker les sections, les clés et les valeurs, et pour lire le fichier en une seule fois.

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();
    }
}
17
Larry

Je souhaite présenter une bibliothèque IniParser que j'ai entièrement créée en c #. Par conséquent, elle ne contient aucune dépendance dans aucun système d'exploitation, ce qui la rend compatible Mono. Open Source avec la licence MIT - afin qu’il puisse être utilisé dans n’importe quel code.

Vous pouvez consultez le code source dans GitHub , et c'est également disponible sous forme de paquet NuGet

C'est très configurable , et vraiment simple à utiliser .

Désolé pour la fiche éhontée mais j'espère que cela pourra aider quiconque revisitant cette réponse.

11
Ricardo Amores

Je suis en retard pour rejoindre le parti, mais j'avais le même problème aujourd'hui et j'ai écrit l'implémentation suivante:

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"];
}

Il est à noter que cette implémentation ne gère pas les sections ou propriétés non trouvées. Pour ce faire, vous devez étendre la classe Dictionary<,>- à la gestion des clés non trouvées.


Pour sérialiser une instance de Dictionary<string, Dictionary<string, string>> dans un fichier .ini-, j'utilise le code suivant:

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());
3
unknown6656

Généralement, lorsque vous créez des applications à l'aide de C # et du framework .NET, vous n'utilisez pas les fichiers INI. Il est plus courant de stocker les paramètres dans un fichier de configuration XML ou dans le registre. Toutefois, si votre logiciel partage les paramètres avec une application existante, il peut être plus facile d’utiliser son fichier de configuration plutôt que de dupliquer les informations ailleurs.

Le framework .NET ne prend pas en charge l’utilisation directe des fichiers INI. Toutefois, vous pouvez utiliser les fonctions de l'API Windows avec les services d'appel de plateforme (P/Invoke) pour écrire et lire dans les fichiers. Dans ce lien, nous créons une classe qui représente les fichiers INI et utilise les fonctions de l'API Windows pour les manipuler. S'il vous plaît passez par le lien suivant.

Lecture et écriture de fichiers INI

3
Unknown

Un analyseur Ini est disponible dans CommonLibrary.NET

Cela a diverses surcharges très pratiques pour obtenir des sections/valeurs et est très léger.

2
james

Voici ma propre version, en utilisant des expressions régulières. Ce code suppose que chaque nom de section est unique. Si ce n'est pas le cas, il est logique de remplacer Dictionnaire par Liste. Cette fonction prend en charge les commentaires de fichier .ini à partir de ';' personnage. Section commence normalement [section], et les paires clé-valeur sont également fournies normalement "clé = valeur". Même hypothèse que pour les sections - le nom de la clé est unique.

/// <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;
}
2
TarmoPikaro

Si vous avez seulement besoin d'un accès en lecture et non d'un accès en écriture et que vous utilisez le _Microsoft.Extensions.Confiuration_ (fourni par défaut avec ASP.NET Core mais fonctionne également avec les programmes classiques), vous pouvez utiliser le package NuGet Microsoft.Extensions.Configuration.Ini pour importer des fichiers ini dans vos paramètres de configuration.

_public Startup(IHostingEnvironment env)
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddIniFile("SomeConfig.ini", optional: false);
    Configuration = builder.Build();
}
_
2
Scott Chamberlain

PeanutButter.INI est une classe emballée par Nuget pour la manipulation de fichiers INI. Il prend en charge la lecture/écriture, y compris les commentaires - vos commentaires sont conservés lors de l’écriture. Il semble être raisonnablement populaire, est testé et facile à utiliser. C'est aussi totalement gratuit et open-source.

Avertissement: je suis l'auteur de PeanutButter.INI.

2
daf

Essayez cette méthode:

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;
}

Il crée le dictionnaire où la clé est "-". Vous pouvez le charger comme ceci:

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

Si vous voulez juste un lecteur simple sans sections et aucune autre DLL, la solution est simple:

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;
        }
    }
}

Exemple d'utilisation:

    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");

Le contenu du fichier de configuration entre-temps (comme vous le voyez supporte le symbole # pour le commentaire de ligne):

#command to run
command = php

#default script
action = index.php

#debug mode
#debug = true

#plain text mode
#plain = false

#icon = favico.ico
1
BIOHAZARD

Voici ma classe, fonctionne comme un charme:

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();

    }

}

L'utilisation est évidente puisqu'il s'agit d'une classe statique, il suffit d'appeler IniFileManager.IniWriteValue pour lire une section ou IniFileManager.IniReadValue pour lire une section.

0
Erwin Draconis