INI 파일 읽기 / 쓰기


263

표준 .ini 파일을 읽고 쓸 수있는 클래스가 .NET 프레임 워크에 있습니까?

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

델파이는 TIniFile구성 요소를 가지고 있으며 C #과 비슷한 것이 있는지 알고 싶습니다.


RemObjects에는 비슷한 INI 파일 클래스를 제공하는 ShineOn이라는 Delphi Prism 라이브러리가 있습니다. 그러나 아직 컴파일 된 어셈블리가 없기 때문에 소스에서 .NET 용으로 컴파일하려면 Delphi Prism이 필요합니다. code.remobjects.com/p/shineon
Lex Li

1
같은 문제가 발생하여 ini 파일을 파싱하기위한 자체 라이브러리를 만들었습니다 : github.com/rickyah/ini-parser 도움이 되길 바랍니다
Ricardo Amores

5
Ricky와 마찬가지로 나는 이것에 대한 나만의 해결책을 만들기로 결정했습니다. 사용 가능한에 : github.com/MarioZ/MadMilkman.Ini
마리오 Z

답변:


185

.NET 프레임 워크 작성자는 INI 파일 대신 XML 기반 구성 파일을 사용하기를 원합니다. 그래서, 그것들을 읽을 수있는 내장 메커니즘이 없습니다.

그러나 사용 가능한 타사 솔루션이 있습니다.


24
XML 구성 파일을 사용하는 것이 사실이지만 이것은 여전히 ​​질문에 대한 대답이 아니거나 링크 전용 VLQ입니다.
Danny Beckett

6
@aloneguid 필자는 사용 가능한 많은 기능이 실제로 .NET 구성 파일에 기여하여 많은 마법을 가진 이상한 거인이 될 것이라고 주장합니다. 그것들은 "설정 파일의 코드"가되었으며, 이로 인해 많은 복잡성과 이상한 동작이 발생하고 구성 관리가 더욱 어려워집니다. (데이터베이스 "제공자"및 연결 문자열을보고 있습니다.) 따라서 INI 파일도 일반적으로 비 수동 편집에 더 좋습니다.
jpmc26

1
나는 오래된 방법 (P / Inovke)을 좋아하고 다음과 같은 오래된 방법으로 유니 코드를 사용할 수 있습니다 : File.WriteAllBytes (path, new byte [] {0xFF, 0xFE});
sailfish009

2
좋은 패키지이지만 더 나을 수 있습니다. '='또는 '\ n'을 포함하는 값을 완전히 구문 분석 할 수 없습니다.
Ahmad Behjati

211

머리말

먼저 INI 파일의 제한 사항대한 이 MSDN 블로그 게시물을 읽으십시오 . 필요에 맞는 경우 계속 읽으십시오.

이것은 원본 Windows P / Invoke를 사용하여 작성한 간결한 구현이므로 .NET이 설치된 모든 Windows 버전 (예 : 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;
        }
    }
}

사용 방법

다음 3 가지 방법 중 하나로 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()방법 이 없습니다 .
STIL

2
어쩌면 더 전통적인 기본값은 응용 프로그램 당 (어셈블리 당이 아닌) .ini 파일 Path.GetFullPath(IniPath ?? Path.ChangeExtension(Application.ExecutablePath, ".ini"))입니다.
유진 Ryabtsev

3
정말 대단해! github에 넣습니까?
Emrys Myrooin

2
@ 대니 베켓, 잘 했어. 이것은 지난 .Net .Net에서 사용한 것과 거의 동일합니다. 몇 년 전에 이전 코드에서 업그레이드되었습니다.
Damian

10
지금은 Raymond Chen을 존중하는 한,이 기사의 많은 제한 사항은 INI 형식 자체가 아니라 Windows의 특정 INI 라이브러리의 제한 사항이었습니다. 세분화 된 권한과 같은 다른 파일은 여러 파일을 통해 쉽게 회피 할 수 있습니다. 공식 , 현대화 INI 라이브러리는 오늘날에도 가장 환영받을 것입니다.
Joel Coehoorn

68

CodeProject에 대한이 기사 " C #을 사용한 INI 파일 처리 클래스 "가 도움이 될 것입니다.

작성자는 KERNEL32.dll에서 두 가지 기능을 제공하는 C # 클래스 "Ini"를 만들었습니다. 이러한 기능은 다음 WritePrivateProfileString과 같습니다 GetPrivateProfileString. 두 개의 네임 스페이스가 필요합니다 : System.Runtime.InteropServicesSystem.Text.

Ini 클래스를 사용하는 단계

프로젝트 네임 스페이스 정의에 추가

using INI;

이와 같은 INIFile을 만듭니다.

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

사용 IniWriteValue섹션 또는 사용의 특정 키에 새 값을 작성하는 IniReadValue특정 섹션의 키에서 값을 읽을 수 있습니다.

참고 : 처음부터 시작하는 경우이 MSDN 문서 : 방법 : C # 프로젝트에 응용 프로그램 구성 파일 추가를 읽을 수 있습니다. 응용 프로그램을 구성하는 더 좋은 방법입니다.


1
완전한 INI 파일을 읽고 싶습니다. 섹션을 읽는 대신 동일한 작업을 수행하는 방법, 키
venkat

이것은 나를 위해 일한 다음 다른 지점에서 일을 중단했습니다. 아직도 후드 아래에서 무엇이 달라 졌는지 전혀 모른다
nawfal

1
더 이상 사용되지 않는 Win32 API 기능을 사용하십시오. 자세한 정보 : stackoverflow.com/questions/11451641/…
Pedro77

나는이 접근법을 잠시 동안 사용했지만 Win7에서 시작된 보안 향상으로 인해이 문제가 거의 사라졌습니다. 이 방법을 계속 사용할 수 있지만 ProgramData에 .ini를 저장하고 앱에서 읽거나 쓰게합니다.
Jess

응용 프로그램 구성 ini 파일을 ProgramData에 저장하지 마십시오. 이들은 Registry 또는 ProgramData에 속하지 않습니다. 구성 파일은 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 당신은 정말로 무엇에 대해 불평합니까? 그는 그것을 찾았다 고 말했다. 당신은 일반적인 접근 자와 stringbuilder 사용법을 찾지 못해서 그를 공감합니까?
Tormod

1
@Tormod : 의견을 줄 이길 바랍니다. 우리가 (의도적으로 긍정적 인) 의도가 아니라 솔루션에 투표 할 때 기술 포럼입니다. Knuth가 게시 한 솔루션에 결함이 있으면 지적해야합니다. 솔루션이 포스터에서 찾거나 작성되었는지는 중요하지 않습니다.
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; }거기에 넣지 않은 것에 감사드립니다
Mark Schultheiss

1
좋은! 더 잘 작동하려면 최소한 일부 변경이 필요합니다. 16 행 : ini [ ""] = currentSection; // ini [ ""] = currentSection; 이 초기화로 인해 첫 번째 요소 [0]이 빈 세그먼트가 될 때마다 제거해야합니다. 36 행 : currentSection [line.Substring (0, idx)] = line.Substring (idx + 1); To : currentSection [line.Substring (0, idx) .Trim ()] = line.Substring (idx + 1) .Trim (); 키와 값은 Trim 줄뿐만 아니라 독립적으로 다듬어야합니다. INI와 같은 구성 파일에서는 일반적으로 K-> V 쌍을 추가하는 부분이 이러한 항목을 섹션 내부에 정렬하는 경향이 있습니다. 감사합니다!
LXSoft

오랜만에. 제안 해 주셔서 감사합니다. 그것들은 모두 의미가 있으며이 코드는 좋은 새로 고침을받을 자격이 있습니다.
Larry

13

c #에서 완전히 만든 IniParser 라이브러리를 소개하고 싶습니다. 따라서 모든 OS에 종속성이 없으므로 Mono와 호환됩니다. MIT 라이센스가있는 오픈 소스이므로 모든 코드에서 사용할 수 있습니다.

당신은 할 수 GitHub의에서 소스를 체크 아웃 하고는 NuGet 패키지로도 제공

그건 크게 구성사용에 정말 간단 .

뻔뻔스런 플러그에 대해 죄송하지만이 답변을 다시 방문하는 사람에게 도움이되기를 바랍니다.


4

읽기 액세스 만 필요하고 쓰기 액세스는 필요하지 않고 Microsoft.Extensions.ConfiurationASP.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 프레임 워크를 사용하여 응용 프로그램을 만들 때는 INI 파일을 사용하지 않습니다. XML 기반 구성 파일이나 레지스트리에 설정을 저장하는 것이 더 일반적입니다. 그러나 소프트웨어가 레거시 응용 프로그램과 설정을 공유하는 경우 다른 곳에서 정보를 복제하지 않고 구성 파일을 사용하는 것이 더 쉬울 수 있습니다.

.NET 프레임 워크는 INI 파일 사용을 직접 지원하지 않습니다. 그러나 P / Invoke (Platform Invocation Services)와 함께 Windows API 함수를 사용하여 파일에 쓰고 읽을 수 있습니다. 이 링크에서는 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 는 INI 파일 조작을위한 Nuget 패키지 클래스입니다. 주석을 포함하여 읽기 / 쓰기를 지원합니다. 주석은 쓰기시 유지됩니다. 그것은 합리적으로 인기있는 것으로 보이며, 테스트되고 사용하기 쉽습니다. 또한 완전히 무료이며 오픈 소스입니다.

면책 조항 : 저는 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<,>키를 처리 하도록 -class를 확장해야 합니다.


의 인스턴스를 직렬화하기 위해 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

CommonLibrary.NET 에는 Ini Parser가 있습니다.

이것은 섹션 / 값을 얻는 데 매우 편리한 다양한 과부하를 가지고 있으며 매우 가볍습니다.


1
라이브러리의 최상위 레벨을 보는 것이 분명하지 않은 경우 (나에게 명확하지 않았습니다!) IniDcoument 클래스 등은 ComLib.IO에 있습니다.
Tim Keating

2
이 경로를보고있는 사람은 CommonLibrary.NET이 .INI 규칙을 따르지 않는 것 같습니다. 등호 대신 구분 기호로 콜론 ":"을 사용하며 주석을 처리하지 않습니다 (세미콜론 또는 파운드 기호가있는 행을 시작하면 구문 분석이 실패 함).
jmmr

2

다음은 정규 표현식을 사용하는 자체 버전입니다. 이 코드는 각 섹션 이름이 고유하다고 가정하지만 사실이 아닌 경우 Dictionary를 List로 바꾸는 것이 좋습니다. 이 함수는 ';'부터 시작하여 .ini 파일 주석 달기를 지원합니다. 캐릭터. 섹션이 정상적으로 시작되고 [섹션] 키 값 쌍도 정상적으로 "key = value"가됩니다. 섹션과 동일한 가정-키 이름은 고유합니다.

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

그 기능은 작동하지 않습니다. [Section] 전에 빈 줄을 사용하거나 사용하지 않고 시도했습니다.
iksess

작동하지 않는 .ini의 예를 복사 할 수 있습니까?
TarmoPikaro

-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를 호출하면됩니다.


이 접근법은 이미 다른 답변 에서 보여지고 설명되었습니다 . 귀하의 답변에 해당되지 않는 내용은 무엇입니까?
Palec

.ini 파일이 UNICODE (16 비트 LE)로 저장된 경우에만 작동합니다. 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

링크 제목은 참조 / 문맥에 대해 매우 명확하다고 생각합니다. 충분하지 않다고 생각되면 자유롭게 편집하십시오.
Daniel

1
실제 질문을 다루지 않습니다.
Erik Knowles
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.