Читання / запис файлу INI


263

Чи є якийсь клас у .NET рамках, який може читати / записувати стандартні файли .ini:

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

У Delphi є TIniFileкомпонент, і я хочу знати, чи є щось подібне для C #?


RemObjects має бібліотеку призми Delphi під назвою ShineOn, яка доставляє схожий клас файлів INI. Але вам потрібно мати призму Delphi, щоб компілювати її для .NET з джерела, оскільки ще немає компільованої збірки. code.remobjects.com/p/shineon
Lex Li

1
З'явилася та ж проблема, і я створив власну бібліотеку для розбору файлів ini: github.com/rickyah/ini-parser Сподіваюся, що це допомагає
Рікардо Аморес

5
Так само, як Рікі, я вирішив самостійно вирішити це. Доступний на сайті: github.com/MarioZ/MadMilkman.Ini
Mario Z

Відповіді:


185

Творці рамки .NET хочуть, щоб ви використовували конфігураційні файли на основі XML, а не файли INI. Так ні, немає вбудованого механізму їх читання.

Однак існують рішення сторонніх розробників.


24
Хоча це правда, що конфігураційні файли XML - це шлях, але це все ще не є відповіддю на питання, або це VLQ лише для посилань.
Денні Бекетт

6
@aloneguid Я можу стверджувати, що великий набір доступних функцій насправді сприяв .NET-конфігураційним файлам, що закінчується дивними бегемотами з великою кількістю магії. Вони стали "кодом у конфігураційному файлі", і це призводить до великої складності, дивного поведінки та ускладнює управління конфігурацією. (Я дивлюся на вас, «постачальників баз даних» та рядків підключення.) Отже, INI-файли також, як правило, краще для редагування, яке не проводиться вручну.
jpmc26

1
мені подобається старий метод (P / Inovke), і ви можете використовувати unicode зі старим методом, як цей: File.WriteAllBytes (шлях, новий байт [] {0xFF, 0xFE});
sailfish009

2
Хороший пакет, але може бути і краще. Він не може проаналізувати значення, яке містить '=' Або '\ n' повністю
Ахмад Бехяті

211

Передмова

По-перше, прочитайте цю публікацію в блозі MSDN про обмеження файлів INI . Якщо це відповідає вашим потребам, читайте далі.

Це стисла реалізація, яку я написав, використовуючи оригінальний Windows P / Invoke, тому він підтримується всіма версіями Windows із встановленою .NET (тобто Windows 98 - Windows 10). Я цим випускаю їх у загальнодоступний домен - ви можете вільно користуватися ним без атрибуції.

Крихітний клас

Додайте новий клас, покликаний IniFile.csу свій проект:

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

Як ним користуватися

Відкрийте файл INI одним із трьох наступних способів:

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

Ви можете записати такі значення, як:

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

Щоб створити такий файл:

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

Щоб прочитати значення з файлу INI:

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

За бажанням можна встановити [Section]:

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

Щоб створити такий файл:

[Audio]
DefaultVolume=100

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

Ви також можете перевірити наявність ключа типу:

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

Ви можете видалити такий ключ, як:

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

Ви також можете видалити цілий розділ (включаючи всі клавіші) так:

MyIni.DeleteSection("Web");

Будь ласка, коментуйте будь-які вдосконалення!


4
Я трохи запізнююся, але це відсутність GetSections()методу.
стиль

2
Можливо, більш традиційним за замовчуванням буде файли .ini на додаток (не за збірку) .ini файли Path.GetFullPath(IniPath ?? Path.ChangeExtension(Application.ExecutablePath, ".ini")).
Євген Рябцев

3
Дійсно чудово ! Покласти його на github?
Emrys Myrooin

2
@danny Beckett, прекрасно зроблено. Це майже точно так само, як те, що я використовував протягом останніх років .Net. Оновлений від старого коду років тому.
Даміан

10
Старе зараз, і наскільки я поважаю Реймонда Чена, багато обмежень у цій статті були обмеженнями певної бібліотеки INI у Windows, а не самим форматом INI. Інші, як-от деталізовані дозволи, можна легко пройти через декілька файлів. Офіційна , модернізовані бібліотека INI буде найбільш вітати, навіть сьогодні.
Joel Coehoorn

68

Ця стаття про CodeProject " Клас обробки файлів INI з використанням C # " повинна допомогти.

Автор створив C # клас "Ini", який відкриває дві функції з KERNEL32.dll. Цими функціями є: WritePrivateProfileStringі GetPrivateProfileString. Вам знадобляться два простору імен: System.Runtime.InteropServicesі System.Text.

Етапи використання класу Ini

У визначення простору імен проекту додайте

using INI;

Створіть такий INIFile, як це

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

Використовуйте IniWriteValueдля того, щоб записати нове значення до певного ключа в розділі або скористатися IniReadValueдля зчитування значення ВІД ключа в певному Розділі.

Примітка. Якщо ви починаєте з нуля, ви можете прочитати цю статтю MSDN : Як: Додати файли конфігурації програми до проектів C # . Це кращий спосіб налаштування програми.


1
Я хочу прочитати повний файл INI. Як зробити те саме, замість того, щоб прочитати розділ, ключ
venkat

це працювало для мене, а потім перестав працювати з іншого пункту. Досі не маю ідеї, що під кришкою пішло інакше
nawfal

1
Слідкуйте за тим, щоб використовувати цю застарілу функцію API Win32. Більш детальна інформація: stackoverflow.com/questions/11451641 / ...
Pedro77

Я використовував такий підхід деякий час, але покращення безпеки, починаючи з Win7, значною мірою вбило це для мене. Ви все ще можете використовувати цей підхід, але ви збережете .ini в ProgramData і ваш додаток буде читати / писати там.
Джесс

Не зберігайте ini-файли конфігурації програми у ProgramData. Вони не належать ні до Реєстру, ні до програми DataData. Файли конфігурації повинні бути у папках LocalApplicationData.
deegee

47

Я знайшов цю просту реалізацію:

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

Добре працює для того, що мені потрібно.

Ось як ви його використовуєте:

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

Ось код:

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

38
+1 для зміщення вище пониженого рівня. На що ти справді скаржишся? Він сказав, що знайшов це. Чи ви вважаєте його за те, що він не знайшов його із загальними аксесуарами та використанням стробобудівників?
Tormod

1
@Tormod: Бажаю, щоб я міг оскаржити коментар. Це технічний форум, коли ми голосуємо за рішення, а не (очевидно позитивний) намір. Якщо рішення, опубліковане самим Кнутом, мало вади, це було б - і повинно бути вказане. Не має значення, чи рішення знайшло або написало плакат.
ya23,

7
Я думаю, ви розтягуєте визначення "недолік". Якщо рішення не підкреслює ваші чутливості, тоді просто не заявляйте. Щойно я залишив записку, в якій говорилося, що я вже заперечував його голос, щоб інші 7 хлопців, які підтримали мій коментар, самі не зробили цього.
Tormod

21

Код у відповіді joerage надихає.

На жаль, він змінює корпус символів клавіш і не обробляє коментарі. Тож я написав щось, що повинно бути достатньо надійним для читання (лише) дуже брудних INI-файлів і дозволяє отримати ключі такими, якими вони є.

Для зберігання розділів, ключів та значень та читання файлу за один раз використовується деякий LINQ, вкладений словник, нечутливий до регістру рядків.

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
і спасибі за те, що не докладаючи catch (Exception ex) { throw ex; }там
Марк Schultheiss

1
Добре! Для кращої роботи потрібні хоча б деякі зміни. Рядок 16: ini [""] = currentSection; До: // ini [""] = currentSection; Це потрібно видалити, оскільки кожен раз, коли перший елемент [0] буде порожнім сегментом через цю ініціалізацію. Рядок 36: currentSection [line.Substring (0, idx)] = line.Substring (idx + 1); До: currentSection [line.Substring (0, idx) .Trim ()] = line.Substring (idx + 1) .Trim (); Ключ і значення повинні бути підрізані незалежно, не тільки на лінії Обрізати. У таких конфігураційних файлах, як INI, зазвичай додають K-> V пари, як правило, вирівнюють ці рівні в розділах. Дякую!
LXSoft

Давно були à. Дякую за ваші пропозиції. Всі вони мають сенс і заслуговують на те, щоб цей код добре оновився.
Ларрі

13

Я хочу представити бібліотеку IniParser, яку я повністю створив у c #, тому вона не містить залежності в жодній ОС, що робить Mono сумісним. Open Source з ліцензією MIT - тому його можна використовувати в будь-якому коді.

Ви можете перевірити джерело в GitHub , воно також доступне як пакет NuGet

Це легко настроюється і дуже простий у використанні .

Вибачте за безсоромний штекер, але я сподіваюся, що це може допомогти тому, хто перегляне цю відповідь.


4

Якщо вам потрібен лише доступ для читання, а не доступ до запису, і ви використовуєте Microsoft.Extensions.Confiuration(постачається за замовчуванням з ASP.NET Core, але працює і з звичайними програмами), ви можете використовувати пакет NuGet Microsoft.Extensions.Configuration.Iniдля імпорту файлів ini у ваші налаштування конфігурації.

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

Просто додам, що потім ви отримуєте ключіConfiguration["keyname"]
kofifus

@scott проблема, з якою я виникає, з будь-якої причини IIS не розпізнає її під час роботи програми. вона розгорнута і там, але не споживається. HTTP 500.30 повернувся, а журнал додатків IIS говорить, що "файл конфігурації не знайдено і не є додатковим".
one.beat.consumer

3

Зазвичай, коли ви створюєте програми за допомогою C # та .NET Framework, ви не будете використовувати файли INI. Частіше зберігається налаштування у файлі конфігурації на основі XML або в реєстрі. Однак якщо ваше програмне забезпечення ділиться налаштуваннями зі застарілим додатком, можливо, буде простіше використовувати його файл конфігурації, а не дублювати інформацію в іншому місці.

.NET Framework не підтримує використання INI-файлів безпосередньо. Однак ви можете використовувати функції Windows API за допомогою служб виклику платформи (P / Invoke) для запису та читання з файлів. У цьому посиланні ми створюємо клас, який представляє файли INI і використовує функції Windows API для маніпулювання ними. Перейдіть за наступним посиланням.

Читання та запис файлів INI


4
Не тримайтеся поза реєстром! Дані конфігурації програми не повинні зберігатися в Реєстрі.
deegee

3

Якщо ви хочете просто прочитувач без розділів та будь-яких інших dll тут, це просте рішення:

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

Зразок використання:

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

Тим часом конфігуруйте вміст файлу (як ви бачите, підтримує # символ для коментаря до рядка):

#command to run
command = php

#default script
action = index.php

#debug mode
#debug = true

#plain text mode
#plain = false

#icon = favico.ico

3

Спробуйте цей метод:

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

Він створює словник, де ключ "-". Ви можете завантажити його так:

var dict = ParseIniDataWithSections(File.ReadAllLines(fileName));

3

PeanutButter.INI - із упаковкою Nuget для обробки файлів INI. Він підтримує читання / запис, включаючи коментарі - ваші коментарі зберігаються під час запису. Він, здається, досить популярний, перевірений і простий у використанні. Це також абсолютно безкоштовно та з відкритим кодом.

Відмова: Я автор PeanutButter.INI.


Чи можете ви надати посилання на документацію PeanutButter.INI?
Shroombaker


3

Я спізнююсь приєднатися до партії, але у мене був той самий випуск сьогодні, і я написав таку реалізацію:

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

Слід зазначити, що ця реалізація не обробляє розділи або властивості, які не знайдені. Щоб досягти цього, слід розширити Dictionary<,>класу -клас для обробки невпевнених клавіш.


Щоб серіалізувати екземпляр Dictionary<string, Dictionary<string, string>>до .ini-file, я використовую такий код:

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

2

Є Ini Parser доступний у CommonLibrary.NET

Це має різні дуже зручні перевантаження для отримання секцій / значень і має дуже малу вагу.


1
У випадку, якщо це не очевидно з огляду на верхній рівень бібліотеки (мені це не було очевидно!), Клас IniDcoument та ін є в ComLib.IO.
Тім Кітінг

2
Для тих, хто дивиться на цей маршрут, CommonLibrary.NET, схоже, не дотримується конвенцій .INI. Він використовує двокрапку ":", як роздільник замість знака рівності, і він не обробляє коментарі (початок рядка зі знаком напів двокрапки або фунта спричинить збій розбору).
jmmr

2

Ось моя власна версія, використовуючи регулярні вирази. Цей код передбачає, що назва кожного розділу унікальна - якщо це неправда - має сенс замінити Словник списком. Ця функція підтримує коментування файлів .ini, починаючи з ';' характер. Розділ починається нормально [розділ], а пари ключових значень також надходять зазвичай "ключ = значення". Те саме припущення, що і для розділів - ім'я ключа є унікальним.

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

Ця функція не працює для мене: вона забуває один розділ на два. Я намагався із [і] без порожніх рядків перед [Розділом].
iksess

чи можете ви скопіювати приклад свого .ini, який не працює?
ТармоПікаро

-3

Ось мій клас, працює як шарм:

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

    }

}

Використання є нав'язливим, оскільки його статичний клас, просто зателефонуйте IniFileManager.IniWriteValue для читання розділу або IniFileManager.IniReadValue для читання розділу.


Цей підхід уже показаний і пояснений в іншій відповіді . Що додає ваша відповідь, яка не охоплена цим?
Палек

Слідкуйте за тим, щоб він працював лише в тому випадку, якщо файл .ini зберігається в UNICODE (16 біт LE). Використовуйте Notepad ++, щоб перетворити текст в унікод, тому що якщо ви збережете його в роботі UTF-8, звичайно. Також ANSI прийнятний, але ви не можете читати наголошені листи
user2991288

-6

Ви повинні читати та записувати дані з файлів xml, оскільки ви можете зберегти цілий об’єкт у xml, а також можете заповнити об’єкт із збереженого xml. Краще просто маніпулювати предметами.

Ось як це зробити: Запишіть дані об’єкта в XML-файл: https://msdn.microsoft.com/en-us/library/ms172873.aspx Прочитайте дані об’єктів з XML-файлу: https://msdn.microsoft. com / en-us / library / ms172872.aspx


1
Посилання на зовнішні ресурси рекомендуються, але, будь ласка, додайте контекст навколо посилання, щоб ваші колеги користувачі мали уявлення про те, що це таке і чому воно існує. Завжди цитуйте найбільш релевантну частину важливого посилання на випадок, якщо цільовий сайт недоступний або виходить назавжди в автономний режим.
davejal

Я вважаю, що заголовки посилань дуже чіткі щодо його посилань / контексту. Якщо ви вважаєте, що недостатньо, не соромтесь відредагувати це.
Даніель

1
Не стосується фактичного питання.
Ерік Ноулз
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.