C# 读写配置文件方案
一个防修改、保持完整性的读写配置文件方案。
ConfigManager.cs
public class ConfigManager
{
private static readonly string ConfigPath = Path.Combine(AppContext.BaseDirectory, "config.dat"); // 配置文件路径,默认存储在程序根目录
private static readonly byte[] HmacKey = Encoding.UTF8.GetBytes("zzjs_key"); // 加密密钥
private Dictionary<string, object> _configData;
private readonly object _lock = new();
public static ConfigManager Instance { get; } = new ConfigManager();
/// <summary>
/// 构造函数
/// </summary>
private ConfigManager()
{
Load();
}
/// <summary>
/// 索引器
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public object? this[string key]
{
get => Get(key);
set => Set(key, value);
}
/// <summary>
/// 获取配置项
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public object? Get(string key)
{
var sections = key.Split(':', StringSplitOptions.RemoveEmptyEntries);
object? current = _configData;
foreach (var section in sections)
{
if (current is Dictionary<string, object> dict && dict.TryGetValue(section, out var next))
current = next;
else
return null;
}
return current;
}
/// <summary>
/// 设置配置项
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void Set(string key, object? value)
{
var sections = key.Split(':', StringSplitOptions.RemoveEmptyEntries);
lock (_lock)
{
var current = _configData;
for (int i = 0; i < sections.Length - 1; i++)
{
var section = sections[i];
if (!current.TryGetValue(section, out var sub) || sub is not Dictionary<string, object>)
{
current[section] = new Dictionary<string, object>();
}
current = (Dictionary<string, object>)current[section];
}
current[sections.Last()] = value!;
}
}
/// <summary>
/// 存储配置文件
/// </summary>
public void Save()
{
lock (_lock)
{
var json = JsonSerializer.Serialize(_configData);
var compressed = Compress(Encoding.UTF8.GetBytes(json));
var signature = HMACSHA256.HashData(HmacKey, compressed);
var finalData = signature.Concat(compressed).ToArray();
File.WriteAllBytes(ConfigPath, finalData);
}
}
/// <summary>
/// 加载配置文件
/// </summary>
private void Load()
{
try
{
if (!File.Exists(ConfigPath))
{
// 如果配置文件不存在,可以选择创建一个默认配置
_configData = [];
Save();
//throw new FileNotFoundException();
}
var bytes = File.ReadAllBytes(ConfigPath);
if (bytes.Length < 32)
throw new Exception("配置文件不完整");
var signature = bytes.Take(32).ToArray();
var content = bytes.Skip(32).ToArray();
var actualSignature = HMACSHA256.HashData(HmacKey, content);
if (!signature.SequenceEqual(actualSignature))
throw new Exception("配置文件已被篡改");
var decompressed = Decompress(content);
_configData = JsonSerializer.Deserialize<Dictionary<string, object>>(decompressed)!;
}
catch
{
// 可以在这里加载默认配置
_configData = [];
System.Windows.MessageBox.Show("配置文件损坏或不存在,已加载默认配置", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
}
}
/// <summary>
/// 压缩配置文件
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
private static byte[] Compress(byte[] data)
{
using var output = new MemoryStream();
using (var gz = new GZipStream(output, CompressionLevel.Optimal))
gz.Write(data, 0, data.Length);
return output.ToArray();
}
/// <summary>
/// 解压缩配置文件
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
private static string Decompress(byte[] data)
{
using var input = new MemoryStream(data);
using var gz = new GZipStream(input, CompressionMode.Decompress);
using var reader = new StreamReader(gz, Encoding.UTF8);
return reader.ReadToEnd();
}
}SysConfig.cs
/// <summary>
/// 系统配置管理
/// </summary>
public static class SysConfig
{
/// <summary>
/// 获取配置项
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static string? Get(string key)=> Get<string>(key);
/// <summary>
/// 获取配置项
/// </summary>
public static T? Get<T>(string key)
{
var value = ConfigManager.Instance.Get(key);
if (value == null) return default;
try
{
if (value is JsonElement jsonElement)
{
value = jsonElement.ToString();
}
return (T)Convert.ChangeType(value, typeof(T));
}
catch
{
return default;
}
}
/// <summary>
/// 设置配置项
/// </summary>
public static void Set(string key, object? value)
{
ConfigManager.Instance.Set(key, value);
}
/// <summary>
/// 设置配置项并保存
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public static void SetAndSave(string key, object? value)
{
ConfigManager.Instance.Set(key, value);
ConfigManager.Instance.Save();
}
/// <summary>
/// 保存配置
/// </summary>
public static void Save()
{
ConfigManager.Instance.Save();
}
}使用示例:
// 获取
SysConfig.Get("FirstConfig");
SysConfig.Get<int>("ConvertIntConfig");
SysConfig.Get<bool>("Convert:FirstBoolVal");
// 设置
SysConfig.Set("FirstConfig", "保存值");
SysConfig.Set("Convert:FirstBoolVal", true);
// 保存配置项
SysConfig.Save();
// 上述[设置]和[保存配置项]可使用下面一步完成
SysConfig.SetAndSave("ConvertIntConfig", 100);
C# 读写配置文件方案
https://wc.sb/22