Files
懒得勤快 b666acd74e 为 ObjectExtensions 添加 ToDictionary 扩展方法
新增 ToDictionary 扩展方法,可将任意对象转换为 Dictionary<string, object>。支持通过 keySelector 委托自定义键名,并兼容对象为 IDictionary 的情况,对每个属性应用 keySelector 生成键。
2026-09-10 14:53:29 +08:00

348 lines
9.6 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Masuit.Tools.Reflection;
namespace Masuit.Tools.Files;
/// <summary>
/// INI文件操作辅助类
/// </summary>
public class IniFile
{
private readonly Dictionary<string, IniSection> _sections;
private readonly StringComparer _stringComparer;
private readonly BoolOptions _boolOptions;
private readonly string _path;
/// <summary>
/// 全局配置节
/// </summary>
public const string DefaultSectionName = "General";
/// <summary>
///
/// </summary>
/// <param name="path">文件路径</param>
/// <param name="comparer"></param>
/// <param name="boolOptions"></param>
public IniFile(string path, StringComparer comparer = null, BoolOptions boolOptions = null)
{
_path = path;
_stringComparer = comparer ?? StringComparer.CurrentCultureIgnoreCase;
_sections = new Dictionary<string, IniSection>(_stringComparer ?? StringComparer.CurrentCultureIgnoreCase);
_boolOptions = boolOptions ?? new BoolOptions();
if (File.Exists(path))
{
using StreamReader reader = new(path, Encoding.UTF8);
IniSection section = null;
while (reader.ReadLine() is { } line)
{
ParseLine(line, ref section);
}
}
}
/// <summary>
/// 重新加载文件
/// </summary>
public void Reload()
{
_sections.Clear();
using StreamReader reader = new(_path, Encoding.UTF8);
IniSection section = null;
while (reader.ReadLine() is { } line)
{
ParseLine(line, ref section);
}
}
private void ParseLine(string line, ref IniSection section)
{
if (string.IsNullOrWhiteSpace(line))
{
return;
}
line = line.Trim();
if (line[0] == ';')
{
return;
}
if (line[0] == '[')
{
var name = line.Trim('[', ']').Trim();
if (name.Length > 0 && !_sections.TryGetValue(name, out section))
{
section = new IniSection(name, _stringComparer);
_sections.Add(section.Name, section);
}
}
else
{
string name, value;
var strs = line.Split(';')[0].Split('=');
if (strs.Length == 1)
{
name = line.Trim();
value = string.Empty;
}
else
{
name = strs[0].Trim();
value = strs.Skip(1).Join("=").Trim();
}
if (name.Length <= 0)
{
return;
}
if (section == null)
{
section = new IniSection(DefaultSectionName, _stringComparer);
_sections.Add(section.Name, section);
}
if (section.TryGetValue(name, out var item))
{
item.Value = value;
}
else
{
item = new IniItem
{
Name = name,
Value = value
};
section.Add(name, item);
}
}
}
/// <summary>
/// 保存配置文件
/// </summary>
public void Save()
{
using StreamWriter writer = new(_path, false, Encoding.UTF8);
bool firstLine = true;
foreach (var section in _sections.Values.Where(section => section.Count > 0))
{
if (firstLine)
{
firstLine = false;
}
else
{
writer.WriteLine();
}
writer.WriteLine($"[{section.Name}]");
foreach (var setting in section.Values)
{
writer.WriteLine(setting.ToString());
}
}
}
/// <summary>
/// 异步保存配置文件
/// </summary>
/// <returns></returns>
public async Task SaveAsync()
{
using StreamWriter writer = new(_path, false, Encoding.UTF8);
var firstLine = true;
foreach (var section in _sections.Values.Where(section => section.Count > 0))
{
if (firstLine)
{
firstLine = false;
}
else
{
await writer.WriteLineAsync();
}
await writer.WriteLineAsync($"[{section.Name}]");
foreach (var setting in section.Values)
{
await writer.WriteLineAsync(setting.ToString());
}
}
}
#region Read values
/// <summary>
/// 获取指定节的指定键的值
/// </summary>
/// <param name="section">节</param>
/// <param name="key">键</param>
/// <param name="defaultValue">获取不到时的默认值</param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
public string GetValue(string section, string key, string defaultValue = null)
{
if (section == null)
{
throw new ArgumentNullException(nameof(section));
}
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
if (!_sections.TryGetValue(section, out var iniSection))
{
return defaultValue;
}
if (iniSection.TryGetValue(key, out var iniSetting))
{
return iniSetting.Value;
}
return defaultValue;
}
/// <summary>
/// 获取指定节的指定键的值
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="section">配置节</param>
/// <param name="key">键</param>
/// <param name="defaultValue">获取不到时的默认值</param>
/// <returns></returns>
public T GetValue<T>(string section, string key, T defaultValue = default) where T : IConvertible
{
return GetValue(section, key).TryConvertTo(defaultValue);
}
/// <summary>
/// 所有的配置节
/// </summary>
/// <returns></returns>
public List<IniSection> GetSections() => _sections.Values.ToList();
/// <summary>
/// 获取指定节的所有键值对
/// </summary>
/// <param name="section">节</param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
public Dictionary<string, string> GetSection(string section = DefaultSectionName)
{
if (section == null)
{
throw new ArgumentNullException(nameof(section));
}
var values = _sections.TryGetValue(section, out var iniSection) ? iniSection.Values : Enumerable.Empty<IniItem>();
return values.ToDictionary(x => x.Name, x => x.Value);
}
/// <summary>
/// 获取指定节的配置并绑定到指定类型
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="section">节</param>
/// <returns></returns>
public T GetSection<T>(string section = DefaultSectionName) where T : class, new()
{
var dic = GetSection(section);
var obj = new T();
var properties = typeof(T).GetProperties().ToDictionary(p => p.GetAttribute<IniPropertyAttribute>()?.Name ?? p.Name,p => p);
foreach (var item in dic.Where(item => properties.ContainsKey(item.Key)))
{
properties[item.Key].SetValue(obj, item.Value.ConvertTo(properties[item.Key].PropertyType));
}
return obj;
}
#endregion Read values
#region Write values
/// <summary>
/// 设置指定节的指定键的值
/// </summary>
/// <param name="section">节</param>
/// <param name="key">键</param>
/// <param name="value">值</param>
/// <exception cref="ArgumentNullException"></exception>
public void SetValue(string section, string key, string value)
{
if (section == null)
{
throw new ArgumentNullException(nameof(section));
}
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
if (!_sections.TryGetValue(section, out var sec))
{
sec = new IniSection(section, _stringComparer);
_sections.Add(sec.Name, sec);
}
if (!sec.TryGetValue(key, out var item))
{
item = new IniItem
{
Name = key
};
sec.Add(key, item);
}
item.Value = value ?? string.Empty;
}
/// <summary>
/// 设置指定节的指定键的值
/// </summary>
/// <param name="section">节</param>
/// <param name="key">键</param>
/// <param name="value">值</param>
public void SetValue(string section, string key, bool value) => SetValue(section, key, _boolOptions.ToString(value));
/// <summary>
/// 设置指定节的指定键的值
/// </summary>
/// <param name="section">节</param>
/// <param name="key">键</param>
/// <param name="value">值</param>
public void SetValue<T>(string section, string key, T value) where T : IConvertible => SetValue(section, key, value.ToString());
/// <summary>
/// 清空配置节
/// </summary>
/// <param name="section"></param>
public void ClearSection(string section)
{
if (_sections.TryGetValue(section, out var sec))
{
sec.Clear();
}
}
/// <summary>
/// 清空所有配置节
/// </summary>
public void ClearAllSection()
{
_sections.Clear();
}
#endregion Write values
}