using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Text.RegularExpressions; using Developpez.Dotnet.Properties; namespace Developpez.Dotnet.Configuration { /// /// Représente un fichier de configuration de type INI /// public class IniFile { #region Membres privés statiques private static readonly Regex _regexSection = new Regex(@"\s*\[\s*([\.\w\s]+)\]", RegexOptions.Compiled); private static readonly Regex _regexKeyValuePair = new Regex(@"^\s*(\w[\s\w]+)=(.*?)(\s*;.*)?$", RegexOptions.Compiled); #endregion #region Membres privés private readonly bool _caseSensitive; private readonly IDictionary> _sections; private Encoding _encoding; #endregion #region Constructeurs /// /// Crée une instance vide de IniFile, pour créer dynamiquement /// le contenu du fichier avant de l'enregistrer. /// public IniFile() : this(null, false) { } /// /// Crée une instance vide de IniFile, pour créer dynamiquement /// le contenu du fichier avant de l'enregistrer. /// /// Indique si le IniFile doit tenir compte de la casse public IniFile(bool caseSensitive) : this(null, caseSensitive) { } /// /// Crée une instance de IniFile en chargeant le fichier spécifié /// /// Nom du fichier INI à ouvrir public IniFile(string fileName) : this(fileName, false) { } /// /// Crée une instance de IniFile en chargeant le fichier spécifié /// /// Nom du fichier INI à ouvrir /// Indique si le IniFile doit tenir compte de la casse public IniFile(string fileName, bool caseSensitive) : this(fileName, caseSensitive, null) { } /// /// Crée une instance de IniFile en chargeant le fichier spécifié /// /// Nom du fichier INI à ouvrir /// Encodage à utiliser pour lire le fichier public IniFile(string fileName, Encoding encoding) : this(fileName, false, encoding) { } /// /// Crée une instance de IniFile en chargeant le fichier spécifié /// /// Nom du fichier INI à ouvrir /// Indique si le IniFile doit tenir compte de la casse /// Encodage à utiliser pour lire le fichier public IniFile(string fileName, bool caseSensitive, Encoding encoding) { _caseSensitive = caseSensitive; _encoding = encoding; var comparer = _caseSensitive ? StringComparer.CurrentCulture : StringComparer.CurrentCultureIgnoreCase; _sections = new Dictionary>(comparer); if (fileName != null) { FileName = fileName; Reload(); } } #endregion #region Propriétés publiques /// /// Le nom du fichier /// public string FileName { get; set; } /// /// Sections du fichier INI, accessibles sous forme d'un dictionnaire de sections /// Chaque section est elle-même un dictionnaire de propriétés /// public IDictionary> Sections { get { return _sections; } } #endregion #region Indexeur /// /// Renvoie la valeur de la propriété spécifiée dans la section spécifiée /// /// Nom de la section, ou une chaine vide /// Nom de la propriété /// La valeur de la propriété demandée, ou null si la /// propriété n'existe pas dans le fichier public string this[string section, string key] { get { return GetValue(section, key); } set { SetValue(section, key, value); } } #endregion #region Méthodes publiques /// /// Charge ou recharge le fichier indiqué par la propriété FileName /// public void Reload() { if (FileName.IsNullOrEmpty()) throw new InvalidOperationException(ExceptionMessages.IniFileNameNotSet); _sections.Clear(); using (StreamReader rd = new StreamReader(FileName, _encoding ?? Encoding.UTF8, true)) { string curSection = ""; string line; Match m; while ((line = rd.ReadLine()) != null) { // Skip comments if (line.Trim().StartsWith(";")) continue; if ((m = _regexSection.Match(line)).Success) { curSection = m.Groups[1].Value; } else if ((m = _regexKeyValuePair.Match(line)).Success) { string key = m.Groups[1].Value; string value = m.Groups[2].Value; IDictionary dSection; if (!Sections.TryGetValue(curSection, out dSection)) dSection = AddSection(curSection); dSection.Add(key, value); } } _encoding = rd.CurrentEncoding; } } /// /// Ajoute une section au fichier INI /// /// Nom de la section à ajouter /// La section créée, sous form d'un dictionnaire public IDictionary AddSection(string name) { Dictionary section; if (_caseSensitive) section = new Dictionary(); else section = new Dictionary(StringComparer.OrdinalIgnoreCase); Sections.Add(name, section); return section; } /// /// Obtient la valeur de la propriété spécifiée dans la section spécifiée /// Si la propriété n'est pas dans une section, passer une chaine vide /// /// Nom de la section, ou une chaine vide /// Nom de la propriété /// Valeur par défaut si la propriété n'existe pas /// a valeur de la propriété demandée, ou defaultValue si la /// propriété n'existe pas dans le fichier public string GetValue(string section, string key, string defaultValue) { if (section == null) section = ""; IDictionary dSection; if (Sections.TryGetValue(section, out dSection)) { string value; if (dSection.TryGetValue(key, out value)) return value; return defaultValue; } return defaultValue; } /// /// Obtient la valeur de la propriété spécifiée dans la section spécifiée /// Si la propriété n'est pas dans une section, passer une chaine vide /// /// Nom de la section, ou une chaine vide /// Nom de la propriété /// a valeur de la propriété demandée, ou null si la /// propriété n'existe pas dans le fichier public string GetValue(string section, string key) { return GetValue(section, key, null); } /// /// Définit la valeur d'une propriété dans une section donnée. /// Pour n'indiquer aucune section, passer une chaine vide. /// Si la section n'existe pas, elle est créée /// /// Nom de la section, ou une chaine vide /// Nom de la propriété /// Valeur de la propriété public void SetValue(string section, string key, string value) { if (section == null) section = ""; IDictionary dSection; if (!Sections.TryGetValue(section, out dSection)) dSection = AddSection(section); dSection[key] = value; } /// /// Enregistre le fichier INI sous le chemin indiqué par la propriété FileName. /// Attention, les commentaires éventuellement présents dans le fichier avant /// le chargement ne sont pas conservés. /// public void Save() { Save(FileName); } /// /// Enregistre le fichier INI sous le chemin indiqué. /// Attention, les commentaires éventuellement présents dans le fichier avant /// le chargement ne sont pas conservés. /// /// Chemin du fichier public void Save(string fileName) { Save(fileName, _encoding); } /// /// Enregistre le fichier INI sous le chemin indiqué. /// Attention, les commentaires éventuellement présents dans le fichier avant /// le chargement ne sont pas conservés. /// /// Chemin du fichier /// Encodage à utiliser pour lire le fichier public void Save(string fileName, Encoding encoding) { if (fileName.IsNullOrEmpty()) throw new InvalidOperationException(ExceptionMessages.IniFileNameNotSet); _encoding = encoding ?? Encoding.UTF8; using (StreamWriter wr = new StreamWriter(fileName, false, _encoding)) { IDictionary noSection; if (Sections.TryGetValue("", out noSection)) { foreach (string key in noSection.Keys) { wr.WriteLine("{0}={1}", key, Sections[""][key]); } wr.WriteLine(); } foreach (string secName in Sections.Keys) { if (secName.Length == 0) continue; wr.WriteLine("[{0}]", secName); foreach (string key in Sections[secName].Keys) { wr.WriteLine("{0}={1}", key, Sections[secName][key]); } wr.WriteLine(); } } } #endregion } }