Đọc / ghi một tập tin INI


263

Có lớp nào trong .NET framework có thể đọc / ghi tệp .ini chuẩn không:

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

Delphi có TIniFilethành phần và tôi muốn biết liệu có gì tương tự cho C # không?


RemObjects có thư viện Delphi Prism gọi là ShineOn vận chuyển một lớp tệp INI tương tự. Nhưng bạn cần phải có Delphi Prism để biên dịch nó cho .NET từ nguồn vì chưa có một bản tổng hợp có sẵn. code.remobjects.com/p/lighton
Lex Li

1
Có cùng một vấn đề và tạo thư viện của riêng tôi để phân tích các tệp ini: github.com/rickyah/ini-parser Hy vọng nó sẽ giúp
Ricardo Amores

5
Giống như Ricky, tôi quyết định đưa ra giải pháp của riêng mình cho vấn đề này. Nó có sẵn trên: github.com/MarioZ/MadMilkman.Ini
Mario Z

Câu trả lời:


185

Những người tạo ra .NET framework muốn bạn sử dụng các tệp cấu hình dựa trên XML, thay vì các tệp INI. Vì vậy, không có cơ chế tích hợp để đọc chúng.

Có những giải pháp của bên thứ ba, mặc dù.

  • Trình xử lý INI có thể được lấy dưới dạng các gói NuGet , chẳng hạn như INI Parser .
  • Bạn có thể viết trình xử lý INI của riêng bạn, đó là cách học cũ, chăm chỉ. Nó cho phép bạn kiểm soát nhiều hơn đối với việc triển khai, mà bạn có thể sử dụng cho mục đích xấu hoặc tốt. Xem ví dụ: lớp xử lý tệp INI bằng C #, P / Gọi và Win32 .

24
Mặc dù đúng là các tệp cấu hình XML là hướng đi, nhưng đây vẫn không phải là câu trả lời cho câu hỏi hoặc là VLQ chỉ dành cho liên kết.
Daniel Beckett

6
@aloneguid Tôi sẽ lập luận rằng tập hợp lớn các tính năng có sẵn thực sự đã đóng góp cho các tệp cấu hình .NET, kết thúc là các khổng lồ kỳ lạ với rất nhiều phép thuật trong đó. Chúng đã trở thành "mã trong tệp cấu hình" và điều này dẫn đến rất nhiều sự phức tạp, các hành vi lạ và khiến việc quản lý cấu hình trở nên khó khăn hơn. (Tôi đang xem xét bạn, "nhà cung cấp" cơ sở dữ liệu và chuỗi kết nối.) Vì vậy, các tệp INI cũng thường tốt hơn cho chỉnh sửa không thủ công.
jpmc26

1
tôi thích phương thức cũ (P / Inovke) và bạn có thể sử dụng unicode với phương thức cũ như thế này: File.WriteAllBytes (đường dẫn, byte mới [] {0xFF, 0xFE});
sailfish009

2
Gói tốt nhưng nó có thể tốt hơn Nó không thể phân tích cú pháp một giá trị chứa '=' Hoặc '\ n' hoàn toàn
Ahmad Behjati

211

Lời nói đầu

Đầu tiên, hãy đọc bài đăng trên blog MSDN này về những hạn chế của các tệp INI . Nếu nó phù hợp với nhu cầu của bạn, hãy đọc tiếp.

Đây là một triển khai ngắn gọn mà tôi đã viết, sử dụng Windows P / Invoke ban đầu, do đó, nó được hỗ trợ bởi tất cả các phiên bản Windows có cài đặt .NET, (ví dụ Windows 98 - Windows 10). Tôi sẽ phát hành nó vào phạm vi công cộng - bạn có thể sử dụng nó một cách thương mại mà không cần ghi công.

Lớp học nhỏ

Thêm một lớp mới được gọi IniFile.csvào dự án của bạn:

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

Làm thế nào để sử dụng nó

Mở tệp INI theo một trong 3 cách sau:

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

Bạn có thể viết một số giá trị như vậy:

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

Để tạo một tệp như thế này:

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

Để đọc các giá trị trong tệp INI:

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

Tùy chọn, bạn có thể đặt [Section]:

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

Để tạo một tệp như thế này:

[Audio]
DefaultVolume=100

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

Bạn cũng có thể kiểm tra sự tồn tại của khóa như vậy:

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

Bạn có thể xóa một khóa như vậy:

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

Bạn cũng có thể xóa toàn bộ phần (bao gồm tất cả các khóa) như vậy:

MyIni.DeleteSection("Web");

Xin vui lòng bình luận với bất kỳ cải tiến!


4
Tôi hơi muộn, nhưng nó thiếu GetSections()phương pháp.
stil

2
Có thể một mặc định truyền thống hơn sẽ là các tệp .ini cho mỗi ứng dụng (không phải mỗi lần lắp ráp) Path.GetFullPath(IniPath ?? Path.ChangeExtension(Application.ExecutablePath, ".ini")).
Eugene Ryabtsev

3
Thực sự tuyệt vời ! Đặt nó trên github?
Emrys Myrooin

2
@danny Beckett, được thực hiện độc đáo. Điều này gần giống hệt như những gì tôi đã sử dụng trong nhiều năm qua của .Net. Nâng cấp từ mã cũ năm trước.
Damian

10
Bây giờ, và nhiều như tôi tôn trọng Raymond Chen, nhiều hạn chế trong bài viết đó là những hạn chế của thư viện INI cụ thể trong Windows, và không phải là định dạng INI. Những người khác, như quyền hạn chi tiết, có thể dễ dàng bỏ qua nhiều tệp. Một thư viện INI chính thức , hiện đại hóa sẽ được hoan nghênh nhất, ngay cả ngày nay.
Joel Coehoorn

68

Bài viết này về CodeProject " Một lớp xử lý tệp INI bằng C # " sẽ giúp ích.

Tác giả đã tạo ra một lớp "Ini" C # hiển thị hai hàm từ KERNEL32.dll. Các chức năng này là: WritePrivateProfileStringGetPrivateProfileString. Bạn sẽ cần hai không gian tên: System.Runtime.InteropServicesSystem.Text.

Các bước sử dụng lớp Ini

Trong định nghĩa không gian tên dự án của bạn thêm

using INI;

Tạo một INIFile như thế này

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

Sử dụng IniWriteValueđể viết một giá trị mới cho một khóa cụ thể trong một phần hoặc sử dụng IniReadValueđể đọc một giá trị TỪ một khóa trong Phần cụ thể.

Lưu ý: nếu bạn bắt đầu từ đầu, bạn có thể đọc bài viết MSDN này : Cách: Thêm tệp cấu hình ứng dụng vào các dự án C # . Đó là một cách tốt hơn để cấu hình ứng dụng của bạn.


1
Tôi muốn đọc tập tin INI hoàn chỉnh. Cách thực hiện tương tự thay vì đọc phần, khóa
venkat

điều này làm việc cho tôi, và sau đó ngừng hoạt động từ một điểm khác. Không có ý tưởng nào khác biệt dưới mui xe
nawfal

1
Xem ra bằng cách sử dụng các hàm Win32 API không dùng nữa. Thêm thông tin: stackoverflow.com/questions/11451641/
Mạnh

Tôi đã sử dụng phương pháp này trong một thời gian, nhưng các cải tiến bảo mật bắt đầu trong Win7 đã giết chết tôi rất nhiều. Bạn vẫn có thể sử dụng phương pháp này, nhưng bạn sẽ lưu trữ .ini trong ProgramData và để ứng dụng của bạn đọc / ghi ở đó.
Jess

Không lưu tập tin ini cấu hình ứng dụng trong ProgramData. Chúng không thuộc về Registry hoặc ProgramData. Các tệp cấu hình được cho là nằm trong các thư mục LocalApplicationData.
deegee

47

Tôi thấy việc thực hiện đơn giản này:

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

Hoạt động tốt cho những gì tôi cần.

Đây là cách bạn sử dụng nó:

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

Đây là mã:

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 để bù trên downvote. Bạn thực sự phàn nàn về điều gì? Anh ấy nói anh ấy đã thành lập nó. Bạn có downvote anh ta vì không tìm thấy một với người truy cập chung và sử dụng chuỗi xây dựng?
Tormod

1
@Tormod: Ước gì tôi có thể downvote bình luận. Đó là một diễn đàn kỹ thuật khi chúng tôi bỏ phiếu cho các giải pháp, không phải mục đích (rõ ràng là tích cực). Nếu một giải pháp được đăng bởi chính Knuth có sai sót thì nó sẽ - và nên - được chỉ ra. Không có vấn đề gì nếu giải pháp được tìm thấy hoặc viết bởi người đăng.
ya23

7
Tôi nghĩ rằng bạn kéo dài định nghĩa của "lỗ hổng". Nếu giải pháp không nhấn mạnh vào sự nhạy cảm của bạn, thì đơn giản là đừng upvote. Tôi chỉ để lại một ghi chú nói rằng tôi đã phủ nhận downvote của anh ấy để 7 người khác nêu lên nhận xét của tôi sẽ không tự làm điều này.
Tormod

21

Các mã trong câu trả lời của joerage là cảm hứng.

Thật không may, nó thay đổi vỏ ký tự của các phím và không xử lý các bình luận. Vì vậy, tôi đã viết một cái gì đó đủ mạnh để đọc (chỉ) các tệp INI rất bẩn và cho phép truy xuất các khóa như hiện tại.

Nó sử dụng một số LINQ, một từ điển chuỗi không nhạy cảm trường hợp lồng nhau để lưu trữ các phần, khóa và giá trị và đọc tệp trong một lần.

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
và cảm ơn bạn vì đã không đặt nó catch (Exception ex) { throw ex; }ở đó
Mark Schultheiss

1
Tốt Ít nhất một số thay đổi được yêu cầu để làm việc tốt hơn. Dòng 16: ini [""] = currentSection; To: // ini [""] = currentSection; Điều này phải được loại bỏ vì mỗi lần phần tử đầu tiên [0] sẽ là một phân đoạn trống do khởi tạo này. Dòng 36: currentSection [line.Sub chuỗi (0, idx)] = line.Sub chuỗi (idx + 1); To: currentSection [line.Subopes (0, idx) .Trim ()] = line.Subopes (idx + 1) .Trim (); Khóa và giá trị phải được cắt độc lập, không chỉ trên đường Trim. Trong INI, các tệp cấu hình thường là những người thêm các cặp K-> V có xu hướng căn chỉnh các giá trị này bằng nhau trong các phần. Cảm ơn bạn!
LXSoft

Đã được một thời gian dài. Cảm ơn rất nhiều cho lời đề nghị của bạn. Tất cả đều có ý nghĩa và xứng đáng với mã này để có một sự làm mới tốt.
Larry

13

Tôi muốn giới thiệu một thư viện IniParser mà tôi đã tạo hoàn toàn trong c #, vì vậy nó không chứa phụ thuộc trong bất kỳ HĐH nào, điều này làm cho nó tương thích Mono. Nguồn mở với giấy phép MIT - vì vậy nó có thể được sử dụng trong bất kỳ mã nào.

Bạn có thể kiểm tra nguồn trong GitHub và nó cũng có sẵn dưới dạng gói NuGet

Đó là cấu hình nặng , và thực sự đơn giản để sử dụng .

Xin lỗi vì phích cắm không biết xấu hổ nhưng tôi hy vọng nó có thể giúp đỡ bất cứ ai xem lại câu trả lời này.


4

Nếu bạn chỉ cần truy cập đọc và không truy cập ghi và bạn đang sử dụng Microsoft.Extensions.Confiuration(đi kèm theo mặc định với ASP.NET Core nhưng cũng hoạt động với các chương trình thông thường), bạn có thể sử dụng gói NuGet Microsoft.Extensions.Configuration.Iniđể nhập tệp ini vào cài đặt cấu hình của mình.

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

Chỉ cần thêm rằng sau đó bạn nhận được các khóa vớiConfiguration["keyname"]
kofifus

@scott vấn đề tôi gặp phải là vì bất kỳ lý do gì IIS không nhận ra nó khi ứng dụng đang chạy. nó được triển khai, và ở đó, nhưng không được tiêu thụ. HTTP 500.30 được trả về và nhật ký Ứng dụng IIS cho biết "không tìm thấy tệp cấu hình và không phải là tùy chọn".
one.beat.consumer

3

Thông thường, khi bạn tạo các ứng dụng bằng C # và .NET framework, bạn sẽ không sử dụng các tệp INI. Việc lưu trữ các cài đặt trong tệp cấu hình dựa trên XML hoặc trong sổ đăng ký là phổ biến hơn. Tuy nhiên, nếu phần mềm của bạn chia sẻ cài đặt với ứng dụng cũ, có thể sử dụng tệp cấu hình của nó dễ dàng hơn thay vì sao chép thông tin ở nơi khác.

.NET framework không hỗ trợ trực tiếp sử dụng các tệp INI. Tuy nhiên, bạn có thể sử dụng các chức năng API của Windows với Dịch vụ gọi nền tảng (P / Gọi) để ghi và đọc từ các tệp. Trong liên kết này, chúng tôi tạo một lớp đại diện cho các tệp INI và sử dụng các hàm API của Windows để thao tác chúng. Vui lòng đi qua liên kết sau.

Đọc và ghi tập tin INI


4
Tránh xa Registry! Dữ liệu cấu hình ứng dụng không nên được lưu trong Registry.
deegee

3

Nếu bạn chỉ muốn một người đọc đơn giản không có phần và bất kỳ dll nào khác ở đây là giải pháp đơn giản:

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

Mẫu sử dụng:

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

Cấu hình nội dung tệp trong khi đó (như bạn thấy hỗ trợ biểu tượng # cho nhận xét dòng):

#command to run
command = php

#default script
action = index.php

#debug mode
#debug = true

#plain text mode
#plain = false

#icon = favico.ico

3

Hãy thử phương pháp này:

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

Nó tạo ra từ điển trong đó khóa là "-". Bạn có thể tải nó như thế này:

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

3

PeanutButter.INI là lớp đóng gói Nuget để thao tác với các tệp INI. Nó hỗ trợ đọc / ghi, bao gồm cả ý kiến ​​- ý kiến ​​của bạn được lưu giữ trên văn bản. Nó dường như là phổ biến hợp lý, được thử nghiệm và dễ sử dụng. Nó cũng hoàn toàn miễn phí và là nguồn mở.

Tuyên bố miễn trừ trách nhiệm: Tôi là tác giả của PeanutButter.INI.


Bạn có thể vui lòng cung cấp một liên kết đến tài liệu PeanutButter.INI không?
Shroombaker


3

Tôi đến trễ để tham gia bữa tiệc, nhưng hôm nay tôi cũng gặp vấn đề tương tự và tôi đã viết cách thực hiện như sau:

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

Cần lưu ý rằng việc triển khai này không xử lý các phần hoặc thuộc tính không được tìm thấy. Để đạt được điều này, bạn nên mở rộng Dictionary<,>lớp để xử lý các khóa không có căn cứ.


Để tuần tự hóa một thể hiện của Dictionary<string, Dictionary<string, string>>một .ini-file, tôi sử dụng đoạn mã sau:

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

Có một Trình phân tích cú pháp Ini có sẵn trong CommonL Library.NET

Điều này có quá tải rất thuận tiện khác nhau để có được các phần / giá trị và trọng lượng rất nhẹ.


1
Trong trường hợp không rõ ràng khi nhìn vào cấp cao nhất của thư viện (điều đó không rõ ràng đối với tôi!), Lớp IniDcoument et al đang ở ComLib.IO.
Tim Keat

2
Đối với bất kỳ ai nhìn vào tuyến đường này, CommonL Library.NET dường như không tuân theo các quy ước .INI. Nó sử dụng dấu hai chấm ":" làm dấu phân cách thay vì dấu bằng và nó không xử lý các nhận xét (bắt đầu một dòng bằng dấu chấm phẩy hoặc dấu hai chấm sẽ khiến phân tích cú pháp thất bại).
jmmr

2

Đây là phiên bản của riêng tôi, sử dụng các biểu thức thông thường. Mã này giả định rằng mỗi tên phần là duy nhất - tuy nhiên điều này không đúng - việc thay thế Từ điển bằng Danh sách là điều hợp lý. Hàm này hỗ trợ nhận xét tệp .ini, bắt đầu từ ';' tính cách. Phần bắt đầu bình thường [phần] và các cặp giá trị khóa cũng thường xuất hiện "key = value". Giả định tương tự như đối với các phần - tên khóa là duy nhất.

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

Chức năng đó không hoạt động, đối với tôi: Nó quên một phần trong hai. Tôi đã thử với và không có dòng trống trước [Phần].
iksess

bạn có thể sao chép ví dụ về .ini của bạn không hoạt động không?
TarmoPikaro

-3

Đây là lớp học của tôi, hoạt động như một cơ duyên:

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

    }

}

Việc sử dụng là obviouse vì nó là một lớp tĩnh, chỉ cần gọi IniFileManager.IniWriteValue để đọc một phần hoặc IniFileManager.IniReadValue để đọc một phần.


Cách tiếp cận này đã được hiển thị và giải thích trong một câu trả lời khác . Câu trả lời của bạn thêm gì mà không được bao phủ bởi câu hỏi đó?
Palec

Coi chừng nó chỉ hoạt động nếu tệp .ini được lưu trong UNICODE (16 bit LE). Sử dụng Notepad ++ để chuyển đổi văn bản thành unicode, bởi vì nếu bạn lưu nó trong UTF-8 sẽ không hoạt động. Ngoài ra ANSI có thể được chấp nhận, nhưng bạn không thể đọc các chữ cái có dấu
user2991288

-6

Bạn nên đọc và ghi dữ liệu từ các tệp xml vì bạn có thể lưu toàn bộ một đối tượng vào xml và bạn cũng có thể điền một đối tượng từ xml đã lưu. Nó là tốt hơn một dễ dàng để thao tác các đối tượng.

Dưới đây là cách thực hiện: Ghi dữ liệu đối tượng vào tệp XML: https://msdn.microsoft.com/en-us/l Library / ms172873.aspx Đọc dữ liệu đối tượng từ tệp XML: https://msdn.microsoft. com / en-us / thư viện / ms172872.aspx


1
Liên kết đến các tài nguyên bên ngoài được khuyến khích, nhưng vui lòng thêm ngữ cảnh xung quanh liên kết để người dùng đồng nghiệp của bạn sẽ có một số ý tưởng về nó là gì và tại sao nó ở đó. Luôn trích dẫn phần có liên quan nhất của một liên kết quan trọng, trong trường hợp trang đích không thể truy cập được hoặc ngoại tuyến vĩnh viễn.
davejal

Tôi tin rằng các tiêu đề liên kết rất rõ ràng về tài liệu tham khảo / bối cảnh của nó. Nếu bạn nghĩ rằng, không đủ cảm thấy thoải mái để chỉnh sửa nó.
Daniel

1
Không giải quyết câu hỏi thực tế.
Erik Knowles
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.