mirror of
https://github.com/ldqk/Masuit.Tools.git
synced 2026-10-18 22:46:15 +08:00
b666acd74e
新增 ToDictionary 扩展方法,可将任意对象转换为 Dictionary<string, object>。支持通过 keySelector 委托自定义键名,并兼容对象为 IDictionary 的情况,对每个属性应用 keySelector 生成键。
348 lines
9.6 KiB
C#
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
|
|
} |