Sfoglia il codice sorgente

重构ini文件操作

懒得勤快 1 anno fa
parent
commit
e9e43c9fc6

+ 0 - 89
Masuit.Tools.Abstractions/Files/INIFile.cs

@@ -1,89 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-using System.Text;
-
-namespace Masuit.Tools.Files
-{
-    /// <summary>
-    /// INI文件操作辅助类,仅支持Windows系统
-    /// </summary>
-    public class INIFile
-    {
-        /// <summary>
-        /// 文件路径
-        /// </summary>
-        public readonly string path;
-
-        /// <summary>
-        /// 传入INI文件路径构造对象
-        /// </summary>
-        /// <param name="iniPath">INI文件路径</param>
-        public INIFile(string iniPath)
-        {
-            path = iniPath;
-        }
-
-        [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")]
-        private static extern int GetPrivateProfileString(string section, string key, string defVal, Byte[] retVal, int size, string filePath);
-
-        /// <summary>
-        /// 写INI文件
-        /// </summary>
-        /// <param name="section">分组节点</param>
-        /// <param name="key">关键字</param>
-        /// <param name="value">值</param>
-        public void IniWriteValue(string section, string key, string value)
-        {
-            WritePrivateProfileString(section, key, value, path);
-        }
-
-        /// <summary>
-        /// 读取INI文件
-        /// </summary>
-        /// <param name="section">分组节点</param>
-        /// <param name="key">关键字</param>
-        /// <returns>值</returns>
-        public string IniReadValue(string section, string key)
-        {
-            StringBuilder temp = new StringBuilder(255);
-            int i = GetPrivateProfileString(section, key, "", temp, 255, path);
-            return temp.ToString();
-        }
-
-        /// <summary>
-        /// 读取INI文件
-        /// </summary>
-        /// <param name="section">分组节点</param>
-        /// <param name="key">关键字</param>
-        /// <returns>值的字节表现形式</returns>
-        public byte[] IniReadValues(string section, string key)
-        {
-            byte[] temp = new byte[255];
-            int i = GetPrivateProfileString(section, key, "", temp, 255, path);
-            return temp;
-        }
-
-        /// <summary>
-        /// 删除ini文件下所有段落
-        /// </summary>
-        public void ClearAllSection()
-        {
-            IniWriteValue(null, null, null);
-        }
-
-        /// <summary>
-        /// 删除ini文件下指定段落下的所有键
-        /// </summary>
-        /// <param name="section">分组节点</param>
-        public void ClearSection(string section)
-        {
-            IniWriteValue(section, null, null);
-        }
-    }
-}

+ 95 - 0
Masuit.Tools.Abstractions/Files/IniFile/BoolOptions.cs

@@ -0,0 +1,95 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Masuit.Tools.Files;
+
+/// <summary>
+/// 布尔类型的特殊处理选项
+/// </summary>
+public class BoolOptions
+{
+    private Dictionary<string, bool> _boolStringLookup;
+    private string _trueString = "true";
+    private string _falseString = "false";
+
+    /// <summary>
+    /// 非0值作为true
+    /// </summary>
+    public bool NonZeroNumbersAreTrue { get; set; }
+
+    /// <summary>
+    /// 构造函数
+    /// </summary>
+    /// <param name="comparer"></param>
+    public BoolOptions(StringComparer comparer = null)
+    {
+        _boolStringLookup = new Dictionary<string, bool>(comparer ?? StringComparer.CurrentCultureIgnoreCase)
+        {
+            [_trueString] = true,
+            [_falseString] = false,
+            ["yes"] = true,
+            ["no"] = false,
+            ["on"] = true,
+            ["off"] = false,
+            ["1"] = true,
+            ["0"] = false,
+            ["y"] = true,
+            ["n"] = false,
+        };
+        NonZeroNumbersAreTrue = true;
+    }
+
+    /// <summary>
+    /// 设置布尔值对应的词语
+    /// </summary>
+    /// <param name="words"></param>
+    /// <exception cref="ArgumentNullException"></exception>
+    /// <exception cref="InvalidOperationException"></exception>
+    public void SetBoolWords(IEnumerable<BoolWord> words)
+    {
+        if (words == null)
+        {
+            throw new ArgumentNullException(nameof(words));
+        }
+
+        var word = words.FirstOrDefault(w => w.Value);
+        if (word == null)
+        {
+            throw new InvalidOperationException("布尔词列表不包含“true”值的条目。");
+        }
+
+        _trueString = word.Word;
+        word = words.FirstOrDefault(w => w.Value == false);
+        if (word == null)
+        {
+            throw new InvalidOperationException("布尔词列表不包含“false”值的条目。");
+        }
+
+        _falseString = word.Word;
+        _boolStringLookup = words.ToDictionary(w => w.Word, w => w.Value);
+    }
+
+    internal string ToString(bool value) => value ? _trueString : _falseString;
+
+    internal bool TryParse(string s, out bool value)
+    {
+        if (s != null)
+        {
+            if (_boolStringLookup.TryGetValue(s, out bool b))
+            {
+                value = b;
+                return true;
+            }
+
+            if (NonZeroNumbersAreTrue && int.TryParse(s, out int i))
+            {
+                value = i != 0;
+                return true;
+            }
+        }
+
+        value = false;
+        return false;
+    }
+}

+ 14 - 0
Masuit.Tools.Abstractions/Files/IniFile/BoolWord.cs

@@ -0,0 +1,14 @@
+namespace Masuit.Tools.Files;
+
+public class BoolWord
+{
+    public string Word { get; set; }
+
+    public bool Value { get; set; }
+
+    public BoolWord(string word, bool value)
+    {
+        Word = word;
+        Value = value;
+    }
+}

+ 348 - 0
Masuit.Tools.Abstractions/Files/IniFile/IniFile.cs

@@ -0,0 +1,348 @@
+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[1].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);
+        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
+}

+ 10 - 0
Masuit.Tools.Abstractions/Files/IniFile/IniItem.cs

@@ -0,0 +1,10 @@
+namespace Masuit.Tools.Files;
+
+public class IniItem
+{
+    public string Name { get; set; }
+
+    public string Value { get; set; }
+
+    public override string ToString() => $"{Name ?? string.Empty}={Value ?? string.Empty}";
+}

+ 13 - 0
Masuit.Tools.Abstractions/Files/IniFile/IniPropertyAttribute.cs

@@ -0,0 +1,13 @@
+using System;
+
+namespace Masuit.Tools.Files;
+
+public class IniPropertyAttribute : Attribute
+{
+    public string Name { get; set; }
+
+    public IniPropertyAttribute(string name)
+    {
+        Name = name;
+    }
+}

+ 9 - 0
Masuit.Tools.Abstractions/Files/IniFile/IniSection.cs

@@ -0,0 +1,9 @@
+using System;
+using System.Collections.Generic;
+
+namespace Masuit.Tools.Files;
+
+public class IniSection(string name, StringComparer comparer) : Dictionary<string, IniItem>(comparer)
+{
+    public string Name { get; set; } = name;
+}

+ 1 - 1
Masuit.Tools.Abstractions/Files/TextEncodingDetector.cs

@@ -3,7 +3,7 @@ using System.Collections.Generic;
 using System.IO;
 using System.Text;
 
-namespace Masuit.Tools.Abstractions.Files;
+namespace Masuit.Tools.Files;
 
 /// <summary>
 /// 字节文本编码检测

+ 2 - 2
Masuit.Tools.Abstractions/Masuit.Tools.Abstractions.csproj

@@ -3,10 +3,10 @@
         <TargetFrameworks>netstandard2.0;netstandard2.1;net461;net5;net6;net7;net8</TargetFrameworks>
         <LangVersion>latest</LangVersion>
         <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
-        <Version>2.6.9.9</Version>
+        <Version>2024.1</Version>
         <Authors>懒得勤快</Authors>
         <Description>全龄段友好的C#万能工具库,码数吐司库,不管你是菜鸟新手还是骨灰级玩家都能轻松上手,Masuit.Tools基础公共库(适用于.NET4.6.1/.NET Standard2.0及以上项目),包含一些常用的操作类,大都是静态类,加密解密,反射操作,Excel简单导出,权重随机筛选算法,分布式短id,表达式树,linq扩展,文件压缩,多线程下载和FTP客户端,硬件信息,字符串扩展方法,日期时间扩展操作,中国农历,大文件拷贝,图像裁剪,验证码,断点续传,集合扩展等常用封装。
-            官网教程:https://tools.masuit.org
+            官网教程:https://masuit.tools
             github:https://github.com/ldqk/Masuit.Tools
         </Description>
         <Copyright>懒得勤快,长空X</Copyright>

+ 1 - 1
Masuit.Tools.AspNetCore/Masuit.Tools.AspNetCore.csproj

@@ -18,7 +18,7 @@
         <Product>Masuit.Tools.AspNetCore</Product>
         <PackageId>Masuit.Tools.AspNetCore</PackageId>
         <LangVersion>latest</LangVersion>
-        <Version>1.2.9.11</Version>
+        <Version>2024.1</Version>
         <RepositoryType></RepositoryType>
         <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
         <FileVersion>1.1.9</FileVersion>

+ 1 - 1
Masuit.Tools.Core/Masuit.Tools.Core.csproj

@@ -6,7 +6,7 @@
 官网教程:https://tools.masuit.org
 github:https://github.com/ldqk/Masuit.Tools
         </Description>
-        <Version>2.6.9.9</Version>
+        <Version>2024.1</Version>
         <Copyright>Copyright © 懒得勤快</Copyright>
         <PackageProjectUrl>https://github.com/ldqk/Masuit.Tools</PackageProjectUrl>
         <PackageTags>Masuit.Tools,工具库,Utility,Crypt,Extensions</PackageTags>

+ 1 - 1
Masuit.Tools.Excel/Masuit.Tools.Excel.csproj

@@ -3,7 +3,7 @@
         <TargetFramework>netstandard2.0</TargetFramework>
         <LangVersion>latest</LangVersion>
         <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
-        <Version>1.2.9.9</Version>
+        <Version>2024.1</Version>
         <Authors>懒得勤快</Authors>
         <Description>Masuit.Tools.Excel导出库,支持一些简单数据的导出,支持图片列</Description>
         <Copyright>懒得勤快</Copyright>

+ 3 - 3
Masuit.Tools.Net45/Masuit.Tools.Net45.csproj

@@ -69,12 +69,12 @@
     <Compile Include="..\Masuit.Tools.Abstractions\Extensions\Dynamics\*\*.*">
       <Link>Extensions\Dynamics\%(RecursiveDir)%(FileName)%(Extension)</Link>
     </Compile>
+    <Compile Include="..\Masuit.Tools.Abstractions\Files\IniFile\*.*">
+      <Link>Files\IniFile\%(RecursiveDir)%(FileName)%(Extension)</Link>
+    </Compile>
     <Compile Include="..\Masuit.Tools.Abstractions\Files\FileExt.cs">
       <Link>Files\FileExt.cs</Link>
     </Compile>
-    <Compile Include="..\Masuit.Tools.Abstractions\Files\INIFile.cs">
-      <Link>Files\INIFile.cs</Link>
-    </Compile>
     <Compile Include="..\Masuit.Tools.Abstractions\Files\TextEncodingDetector.cs">
       <Link>Files\TextEncodingDetector.cs</Link>
     </Compile>

+ 1 - 1
Masuit.Tools.Net45/package.nuspec

@@ -2,7 +2,7 @@
 <package>
     <metadata>
         <id>Masuit.Tools.Net45</id>
-        <version>2.6.9.9</version>
+        <version>2024.1</version>
         <title>Masuit.Tools</title>
         <authors>懒得勤快</authors>
         <owners>masuit.com</owners>

+ 3 - 3
Masuit.Tools/Masuit.Tools.csproj

@@ -64,12 +64,12 @@
     <Compile Include="..\Masuit.Tools.Abstractions\Files\FileDetector\*\*.*">
       <Link>Files\FileDetector\%(RecursiveDir)%(FileName)%(Extension)</Link>
     </Compile>
+    <Compile Include="..\Masuit.Tools.Abstractions\Files\IniFile\*.*">
+      <Link>Files\IniFile\%(RecursiveDir)%(FileName)%(Extension)</Link>
+    </Compile>
     <Compile Include="..\Masuit.Tools.Abstractions\Files\FileExt.cs">
       <Link>Files\FileExt.cs</Link>
     </Compile>
-    <Compile Include="..\Masuit.Tools.Abstractions\Files\INIFile.cs">
-      <Link>Files\INIFile.cs</Link>
-    </Compile>
     <Compile Include="..\Masuit.Tools.Abstractions\Files\TextEncodingDetector.cs">
       <Link>Files\TextEncodingDetector.cs</Link>
     </Compile>

+ 1 - 1
Masuit.Tools/package.nuspec

@@ -2,7 +2,7 @@
 <package>
     <metadata>
         <id>Masuit.Tools.Net</id>
-        <version>2.6.9.9</version>
+        <version>2024.1</version>
         <title>Masuit.Tools</title>
         <authors>懒得勤快</authors>
         <owners>masuit.com</owners>

+ 17 - 4
README.md

@@ -1041,14 +1041,27 @@ var s="aa".ToSBC(); // 转换为全角
 var s="aa".ToDBC(); // 转换为半角
 ```
 
-### 35.INI配置文件操作(仅支持Windows)
+### 35.INI配置文件操作
 
 ```csharp
-INIFile ini=new INIFile("X:\\filename.ini"); // 需要绝对路径,否则会写到C:\Windows目录下去
-ini.IniWriteValue(section,key,value); // 写值
-ini.IniReadValue(section,key); // 读值
+IniFile ini=new IniFile("X:\\filename.ini"); // 需要绝对路径,否则会写到C:\Windows目录下去
+ini.SetValue(section,key,value); // 写值
+var value=ini.GetValue(section,key); // 读值
+var value=ini.GetValue<Enum>(section,key); // 读值并转换类型
+var sections=ini.GetSections(); // 获取所有配置节
+var section=ini.GetSection(section); // 获取配置节
+var myclass=ini.GetSection<MyClass>(section); // 获取指定配置节并绑定到对象
 ini.ClearAllSection(); // 清空所有配置节
 ini.ClearSection(section); // 清空配置节
+ini.Save(); // 保存ini文件
+ini.Reload(); // 重新加载ini文件
+
+
+class MyClass
+{
+    [IniProperty("str_value")] // 设置别名
+    public string StringValue { get; set; }
+}
 ```
 
 ### 36.雷达图计算引擎