C# 读取层次化ini配置文件

ConnectionStrings:DefaultConnection=DataSource=app.db;Cache=Shared
Logging:LogLevel:Default=Information
Logging:LogLevel:Microsoft=Warning
Logging:LogLevel:Microsoft.Hosting.Lifetime=Information
AllowedHosts=*
using System;
using System.Collections.Generic;
using System.IO;

public class HierarchicalConfig
{
    // 使用不区分大小写的字典存储键值对
    private readonly Dictionary<string, string> _values = 
        new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

    /// <summary>
    /// 从文件加载配置
    /// </summary>
    /// <param name="filePath">配置文件路径</param>
    /// <param name="encoding">文件编码,默认 UTF-8</param>
    public HierarchicalConfig(string filePath, Encoding encoding = null)
    {
        if (encoding == null) encoding = Encoding.UTF8;
        Load(filePath, encoding);
    }

    private void Load(string filePath, Encoding encoding)
    {
        if (!File.Exists(filePath))
            throw new FileNotFoundException("配置文件不存在", filePath);

        foreach (string line in File.ReadAllLines(filePath, encoding))
        {
            string trimmed = line.Trim();

            // 忽略空行和注释(支持 # 和 ; 开头的注释)
            if (string.IsNullOrEmpty(trimmed) || trimmed.StartsWith("#") || trimmed.StartsWith(";"))
                continue;

            int equalIndex = trimmed.IndexOf('=');
            if (equalIndex <= 0) // 没有等号或等号在开头(无效)
                continue;

            string key = trimmed.Substring(0, equalIndex).TrimEnd();  // 键,保留原始大小写
            string value = trimmed.Substring(equalIndex + 1).TrimStart();

            // 可选:去除值两端的引号(如果值用引号包围)
            if (value.Length >= 2)
            {
                if ((value[0] == '"' && value[^1] == '"') || (value[0] == '\'' && value[^1] == '\''))
                    value = value.Substring(1, value.Length - 2);
            }

            // 存储完整键路径(保留冒号分隔符)
            _values[key] = value;
        }
    }

    /// <summary>
    /// 获取指定键的值
    /// </summary>
    /// <param name="keyPath">键路径,如 "Logging:LogLevel:Default"</param>
    /// <param name="defaultValue">键不存在时返回的默认值</param>
    public string GetValue(string keyPath, string defaultValue = null)
    {
        return _values.TryGetValue(keyPath, out string value) ? value : defaultValue;
    }

    /// <summary>
    /// 判断指定键是否存在
    /// </summary>
    /// <param name="keyPath">键路径,如 "ConnectionStrings:DefaultConnection"</param>
    public bool ContainsKey(string keyPath)
    {
        return _values.ContainsKey(keyPath);
    }

    /// <summary>
    /// 获取所有以某前缀开头的键值对(可用于模拟“节”)
    /// </summary>
    /// <param name="prefix">前缀,如 "Logging:"</param>
    public IEnumerable<KeyValuePair<string, string>> GetValuesWithPrefix(string prefix)
    {
        foreach (var kv in _values)
        {
            if (kv.Key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
                yield return kv;
        }
    }
}
posted @ 2026-03-04 11:41  Timskt  阅读(8)  评论(0)    收藏  举报