292 votes

Lecture/écriture d'un fichier INI

Existe-t-il une classe dans le cadre .NET qui peut lire/écrire les fichiers .ini standard ?

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

Delphi a le TIniFile et je voudrais savoir s'il existe quelque chose de similaire pour le C# ?

0 votes

RemObjects a une bibliothèque Delphi Prism appelée ShineOn qui fournit une classe de fichier INI similaire. Mais vous devez disposer de Delphi Prism pour la compiler pour .NET à partir des sources, car il n'existe pas encore d'assemblage compilé disponible. code.remobjects.com/p/shineon

2 votes

J'ai eu le même problème et j'ai créé ma propre bibliothèque pour analyser les fichiers ini : github.com/rickyah/ini-parser J'espère que cela vous aidera

5 votes

Tout comme Ricky, j'ai décidé de trouver ma propre solution à ce problème. Elle est disponible sur : github.com/MarioZ/MadMilkman.Ini

277voto

Danny Beckett Points 6431

Tout d'abord, lisez ce billet de blog MSDN sur les limites des fichiers INI . Si cela vous convient, poursuivez votre lecture.

Il s'agit d'une implémentation concise que j'ai écrite, utilisant le P/Invoke original de Windows (supporté par toutes les versions de Windows avec .NET, y compris Win8). Elle est publiée dans le domaine public.

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 10
    {
        string Path;
        string EXE = Assembly.GetExecutingAssembly().GetName().Name;

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

        [DllImport("kernel32")]
        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;
        }
    }
}

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

En option, vous pouvez définir [Section] 's :

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 suit :

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

Vous pouvez supprimer une touche comme suit :

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

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

MyIni.DeleteSection("Web");

N'hésitez pas à commenter toute amélioration !

9 votes

J'ai un peu de retard, mais il manque. GetSections() méthode.

3 votes

Peut-être qu'un défaut plus traditionnel serait des fichiers .ini par application (et non par assemblage) du type Path.GetFullPath(IniPath ?? Path.ChangeExtension(Application.ExecutablePath, ".ini")) .

3 votes

Vraiment génial ! Mettez-le sur github ?

207voto

David Arno Points 15499

Les créateurs de la structure .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.

Il existe cependant des solutions tierces. Jetez-y un coup d'œil :

http://www.codeproject.com/KB/cs/cs_ini.aspx et
http://jachman.wordpress.com/2006/09/11/how-to-access-ini-files-in-c-net/

7 votes

@aloneguid Je dirais que le grand nombre de fonctionnalités disponibles a en fait contribué à ce que les fichiers de configuration .NET finissent par être des mastodontes étranges avec beaucoup de magie en eux. Ils sont devenus du "code dans le fichier de configuration", ce qui entraîne une grande complexité, des comportements étranges et rend la gestion de la configuration plus difficile. (Je vous regarde, les "fournisseurs" de base de données et les chaînes de connexion). Les fichiers INI sont donc aussi généralement meilleurs pour une édition non manuelle.

1 votes

J'aime l'ancienne méthode (P/Inovke) et vous pouvez utiliser l'unicode avec l'ancienne méthode comme ceci : File.WriteAllBytes(path, new byte[] { 0xFF, 0xFE }) ;

2 votes

Bon paquet mais il pourrait être meilleur. Il ne peut pas analyser une valeur qui contient '=' ou '='. \n complètement

66voto

splattne Points 48126

Cet article sur CodeProject " Une classe de gestion des fichiers INI en C# " devrait 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 .

Marche à suivre pour utiliser la classe Ini

Dans la définition de l'espace de nom de votre projet, ajoutez

using INI;

Créez un INIFile comme ceci

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

Utilisez IniWriteValue pour écrire une nouvelle valeur dans une clé spécifique d'une section ou utiliser la fonction IniReadValue pour lire une valeur à partir d'une clé dans une section spécifique.

Note : si vous partez de zéro, vous pouvez lire ceci Article de MSDN : Comment faire : Ajouter des fichiers de configuration d'application aux projets C# . C'est une meilleure façon de configurer votre application.

1 votes

Je veux lire le fichier INI complet. Comment faire la même chose au lieu de lire la section et la clé ?

0 votes

Cela a fonctionné pour moi, puis a cessé de fonctionner à partir d'un autre point. Aucune idée de ce qui a changé sous le capot

2 votes

Attention à l'utilisation de ces fonctions dépréciées de l'API Win32. Plus d'informations : stackoverflow.com/questions/11451641/

49voto

joerage Points 2603

J'ai trouvé cette mise en œuvre simple :

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

Fonctionne bien pour ce dont j'ai besoin.

Voici comment l'utiliser :

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

41 votes

+1 pour compenser le downvote ci-dessus. De quoi vous plaignez-vous vraiment ? Il a dit qu'il l'avait TROUVÉE. Est-ce que vous le downvotez pour ne pas en avoir trouvé un avec des accesseurs génériques et l'utilisation de stringbuilder ?

1 votes

@Tormod : J'aimerais pouvoir rétrograder le commentaire. C'est un forum technique où l'on vote sur les solutions, pas sur l'intention (évidemment positive). Si une solution postée par Knuth lui-même avait des défauts, elle serait - et devrait - être signalée. Peu importe que la solution ait été trouvée ou écrite par le posteur.

8 votes

Je pense que vous élargissez la définition de "défaut". Si la solution ne met pas en avant vos sensibilités, alors il suffit de ne pas upvoter. J'ai juste laissé une note disant que j'avais déjà annulé sa downvote afin que les 7 autres personnes qui ont upvoté mon commentaire ne le fassent pas elles-mêmes.

23voto

Larry Points 6257

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

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

Il utilise un peu de LINQ, un dictionnaire imbriqué de chaînes de caractères insensibles à la casse pour stocker les sections, les clés et les valeurs, et lit 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();
    }
}

4 votes

Et merci de ne pas mettre ça catch (Exception ex) { throw ex; } là-dedans

2 votes

Bien ! Au moins quelques modifications sont nécessaires pour que le système fonctionne mieux. Ligne 16 : ini[""] = currentSection ; A : //ini[""] = currentSection ; Ceci doit être supprimé car à chaque fois le premier élément [0] sera un segment vide à cause de cette initialisation. Ligne 36 : currentSection[line.Substring(0, idx)] = line.Substring(idx + 1) ; To : currentSection[line.Substring(0, idx).Trim()] = line.Substring(idx + 1).Trim() ; La clé et les valeurs devraient être coupées indépendamment, pas seulement sur la ligne Trim. Dans les fichiers de configuration de type INI, ceux qui ajoutent des paires K->V ont tendance à aligner ces égalités à l'intérieur des sections. Merci !

0 votes

Ça fait longtemps qu'on est là. Merci beaucoup pour vos suggestions. Elles ont toutes du sens et méritent que ce code ait un bon rafraîchissement.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X