.NET平台开源项目速览(5)深入使用与扩展SharpConfig组件

上个月在文章:这些.NET开源项目你知道吗?让.NET开源来得更加猛烈些吧  和 .NET平台开源项目速览(1)SharpConfig配置文件读写组件 中都提到了SharpConfig组件,简单轻量级,速度快,而且还有比较深入的使用介绍。在文章发布后,也有网友提到一些问题,当时我也没仔细去分析,在这次我亲自使用的过程中,就对几个问题进行了比较深入的研究,同时对不满足自己的地方,也进行了扩展。所以今天就把对SharpConfig的源码进行一个简单的分析,同时也根据需求对自己的一个特殊情况进行扩展。自己动手丰衣足食。。。

.NET开源目录:【目录】本博客其他.NET开源项目文章目录

 本文原文地址:.NET平台开源项目速览(5)深入使用与扩展SharpConfig组件

1.SharpConfig源码分析

  SharpConfig源码并不大,核心代码其实也很简单,就是文件读写,解析。在深入使用和扩展SharpConfig之前,有必要了解一下它的基本结构。所以先来介绍SharpConfig源码中核心的3大类。

   Configuration是核心类,我们在前面的文章中只是简单的介绍了一下如何加载配置文件,查看源代码,可以发现加载和保存的方法都是匹配的,都可以从文件或者数据流中加载或者保存。

     由于每一个配置文件都包含若干个Section节,所以也可以使用索引来获取这些节,然后操作节下面的设置项。总的来说思路是很简单的。  

  Configuration在解析过程中,每碰到一个Section,就添加到列表中。而Section的区分就是靠Name,所以,我们在配置文件中注意不要把Section的名称搞混淆了。Section源码中没有特别需要注意的地方,主要是这里检测和移除节点的方法,如下面代码:

 1 /// <summary>检测节中是否存在某个特定名称的设置 </summary>
 2 /// <param name="settingName">设置项的名称</param>
 3 /// <returns>True if the setting is contained in the section; false otherwise.</returns>
 4 public bool Contains(string settingName)
 5 {
 6     return GetSetting(settingName) != null;
 7 }
 8
 9 /// <summary>从本节中移除某个名称的设置</summary>
10 public void Remove(string settingName)
11 {
12     if (string.IsNullOrEmpty(settingName))
13         throw new ArgumentNullException("settingName");
14
15     var setting = GetSetting(settingName);
16
17     if (setting == null)
18     {
19         throw new ArgumentException("The specified setting does not exist in the section.");
20     }
21     mSettings.Remove(setting);
22 }

  每一个Section下面可以有多个Setting设置。下面看看Setting类的情况。 Setting主要是获取和设置值的方法,如代码: 

var someInteger = section["SomeInteger"].GetValue<Boolean>();
float someFloat = section["SomeFloat"].GetValue<float>();

    什么只是简单的对SharpConfig 的结构做一个分析,下面我们将针对问题进行跟深入的分析和修改。

2.SharpConfig使用问题与扩展

2.1 读取乱码的问题

  第一次发现这个问题并不是我,是网友在看完我的文章介绍后使用,发现读取出来是乱码,不能解析。然后反馈给我。其实问题很简单,只是我也没有注意,其实读取的时候也多个方法可以选择, 默认使用的是null编码设置,系统自动检测,但这非常不保险。最好还是自己把文件的编码写进去。例如:

Configuration config = Configuration.LoadFromFile("example.ini", Encoding.GetEncoding("gb2312"));

这样修改后,如果配置文件中有中文,一般是没问题的。非常重要的一点,如果你读取的时候用了固定编码,修改配置值需要保存的时候,也一定要加上编码,否则会导致其他的配置都发生乱码的情况。如下面的代码:

config.Save("example.ini", Encoding.GetEncoding("gb2312"));

  编码的问题,我们可以看一下源码中的情况:

 1 /// <summary>从配置文件直接加载,自动检测编码类型,以及使用默认的设置</summary>
 2 /// <param name="filename">本地配置文件名称</param>
 3 public static Configuration LoadFromFile(string filename)
 4 {
 5     return LoadFromFile(filename, null);
 6 }
 7
 8 /// <summary>从配置文件直接加载</summary>
 9 /// <param name="filename">本地配置文件名称</param>
10 /// <param name="encoding">文件的编码类型,如果为Null,则自动检测</param>
11 public static Configuration LoadFromFile(string filename, Encoding encoding)
12 {
13     if (!File.Exists(filename))
14         throw new FileNotFoundException("Configuration file not found.", filename);
15
16     Configuration cfg = null;
17
18     if (encoding == null)
19         cfg = LoadFromText(File.ReadAllText(filename));
20     else
21         cfg = LoadFromText(File.ReadAllText(filename, encoding));
22
23     return cfg;
24 }

2.2 需要赋空值的情况

  碰到这个问题,可能有些变态吧。其实并不是一个问题,如果需要是String,建议直接写一个固定的值,在后台读取的时候进行判断,因为SharpConfig处理的时候,会剔除前后的空白字符。所以这种情况你直接给空字符串是不可取 的,给一个 null,然后后台判断是否==null,然后进行对应操作;如果是数值类型,也可以特定的设置一个值,比如为0,转换 的时候 判断是否为0,否则作为空处理。

2.3 #注释符与字符串冲突的问题

  在SharpConfig中,其实有一个可以定义注释符的地方。

/// <summary>获取或者设置 注释标识字符</summary>
public static char[] ValidCommentChars
{
    get { return mValidCommentChars; }
    set
    {
        if (value == null) throw new ArgumentNullException("value");
        if (value.Length == 0)
        {
            throw new ArgumentException("The comment chars array must not be empty.","value");
        }
        mValidCommentChars = value;
    }
}

  在配置类的静态构造函数中,默认给了这几个字符作为标识符:

//静态构造函数,设置这些默认值,因此可以修改
static Configuration()
{
    mNumberFormat = CultureInfo.InvariantCulture.NumberFormat;
    mValidCommentChars = new[] { '#', ';', '\'' };
    mIgnoreInlineComments = false;
    mIgnorePreComments = false;
}

  所以如果配置文件中值可能会出现#号的情况,那你就找一个不出现的 字符,来单独作为你的注释标记符,给这个静态属性赋值即可。

2.4 字符串需要换行的问题

  这个问题也很有意思。如果是一行固定文本,你放在配置文件,会自动显示换行,但是读取的时候,人家是看做一行的。因为没有换行符结尾。而如果有几段字符,换行符分割开了,这个时候SharpConfig是肯定不支持的,我们可以看一下SharpConfig中核心的解析函数:

 1 //根据字符串解析配置文件,核心的解析函数
 2 private static Configuration Parse(string source)
 3 {
 4     //重置临时字段
 5     mLineNumber = 0;
 6
 7     Configuration config = new Configuration();
 8     Section currentSection = null;
 9     var preComments = new List<Comment>();
10
11     using (var reader = new StringReader(source))
12     {
13         string line = null;
14
15         // 读取一行,直到结尾(Read until EOF.)
16         while ((line = reader.ReadLine()) != null)
17         {
18             mLineNumber++;
19             //删除前后空白字符
20             line = line.Trim();
21
22             //这里扩展核心的换行支持,使用 3个 ... 开头,说明是上一个设置的换行
23             //每一次行都读取下一行试一下,如果有...,就添加
24             if(line.StartsWith("..."))
25             {
26                 var text = "\r\n" + line.Substring(3);
27                 currentSection[currentSection.SettingCount - 1].Value += text;
28                 continue;
29             }
30             //如果是空行跳过
31             if (string.IsNullOrEmpty(line)) continue;
32
33             int commentIndex = 0;
34             var comment = ParseComment(line, out commentIndex);
35
36             if (!mIgnorePreComments && commentIndex == 0)
37             {
38                 // 解析注释行,添加到 注释列表中去
39                 preComments.Add(comment);
40                 continue;
41             }
42             else if (!mIgnoreInlineComments && commentIndex > 0)
43             {
44                 // 去掉这一行的注释
45                 line = line.Remove(commentIndex).Trim();
46             }
47
48             //如果开始字符是 [ ,说明是 节(Sections)
49             if (line.StartsWith("["))
50             {
51                 #region 节解析
52                 currentSection = ParseSection(line);
53
54                 if (!mIgnoreInlineComments)
55                     currentSection.Comment = comment;
56
57                 if (config.Contains(currentSection.Name))
58                 {
59                     throw new ParserException(string.Format(
60                         "The section '{0}' was already declared in the configuration.",
61                         currentSection.Name), mLineNumber);
62                 }
63
64                 if (!mIgnorePreComments && preComments.Count > 0)
65                 {
66                     currentSection.mPreComments = new List<Comment>(preComments);
67                     preComments.Clear();
68                 }
69
70                 config.mSections.Add(currentSection);
71                 #endregion
72             }
73             else  //否则就是键值设置行
74             {
75                 //解析设置行
76                 Setting setting = ParseSetting(line);
77
78                 if (!mIgnoreInlineComments) setting.Comment = comment;
79
80                 if (currentSection == null) throw new ParserException(string.Format("The setting '{0}' has to be in a section.", setting.Name), mLineNumber);
81
82                 if (currentSection.Contains(setting.Name)) throw new ParserException(string.Format("The setting '{0}' was already declared in the section.", setting.Name), mLineNumber);
83
84                 if (!mIgnorePreComments && preComments.Count > 0)
85                 {
86                     setting.mPreComments = new List<Comment>(preComments);
87                     preComments.Clear();
88                 }
89                 currentSection.Add(setting);
90             }
91
92         }
93     }
94     return config;
95 }

View Code

  上面我进行了注释的翻译,从流程可以看到,SharpConfig是依次读取每一行直接进行转换,看看满足什么特征,然后进行处理。如果直接换行,没有Name和=号对应,那会报错。所以我们自己动手,扩展一下,其实非常简单。

  上述代码是我已经扩展好的,思路很简单,我们选得一个标记字符串,这里使用“...”作为值换行的标记,每一次读取新行的值后,我们先进行换行判断,如果包含"...",就默认作为当前节最后一个Setting的附加值,手动加上换行符"\r\n"。所以核心的代码其实很简单,主要是你要搞清楚流程,要加在哪里:

//这里扩展核心的换行支持,使用 3个 ... 开头,说明是上一个设置的换行
//每一次行都读取下一行试一下,如果有...,就添加
if(line.StartsWith("..."))
{
    var text = "\r\n" + line.Substring(3);
    currentSection[currentSection.SettingCount - 1].Value += text;
    continue;
}

  我们看一个例子,来测试一下换行值的情况,下面是配置文件:

控制台直接读取这个值的代码:

//按文件名称加载配置文件
Configuration config = Configuration.LoadFromFile("example.ini",
                        Encoding.GetEncoding("gb2312"));
Section section = config["General"];
string someString = section["SomeString"].Value;
Console.WriteLine("字符串SomeString值:{0}", someString);

结果如下,已经默认进行换行了:

3.资源

  现在写博客频繁了,也有大量代码,所以开始使用github,这次就作为我的第一个开源项目代码吧,把我修改后的源码发在上面,大家去下载好了。

另外,我也对SharpConfig进行了翻译,可以便于大家更方便的使用,源码可以去github的地址下载,帮助文档也在里面哦。这里先截个图:

这个帮助文档也是使用:.NET平台开源项目速览(4).NET文档生成工具ADB及使用 文章中的ADB工具来生成的,非常好用。

文档和源码下载地址:https://github.com/asxinyu/Improved_SharpConfig

时间: 2024-08-30 16:34:13

.NET平台开源项目速览(5)深入使用与扩展SharpConfig组件的相关文章

.NET平台开源项目速览(2)Compare .NET Objects对象比较组件

    .NET平台开源项目速览今天介绍一款小巧强大的对象比较组件.可以更详细的获取2个对象的差别,并记录具体差别,比较过程和要求可以灵活配置. .NET开源目录:[目录]本博客其他.NET开源项目文章目录       本文地址:.NET平台开源项目速览(2)Compare .NET Objects对象比较组件 1.Compare .NET Objects介绍     Compare .NET Objects组件是.NET平台用于深入比较2个.NET对象的开源组件,一直在更新,主要功能如名字所示

.NET平台开源项目速览(4).NET文档生成工具ADB及使用

原文:.NET平台开源项目速览(4).NET文档生成工具ADB及使用     很久以前就使用ADB这个工具来生成项目的帮助文档.功能强大,在学习一些开源项目的过程中,官方没有提供CHM帮助文档,所以为了快速的了解项目结构和注释.就生成文档来自己看,非常好用.这也是一个学习方法吧.例如本文在: .NET平台开源项目速览(2)Compare .NET Objects对象比较组件 .NET平台开源项目速览(3)小巧轻量级NoSQL文件数据库LiteDB 上述2篇文章中最后的资源中就手动制作了CHM帮助

[置顶].NET平台开源项目速览(4).NET文档生成工具ADB及使用

    很久以前就使用ADB这个工具来生成项目的帮助文档.功能强大,在学习一些开源项目的过程中,官方没有提供CHM帮助文档,所以为了快速的了解项目结构和注释.就生成文档来自己看,非常好用.这也是一个学习方法吧.例如本文在: .NET平台开源项目速览(2)Compare .NET Objects对象比较组件 .NET平台开源项目速览(3)小巧轻量级NoSQL文件数据库LiteDB 上述2篇文章中最后的资源中就手动制作了CHM帮助文档.有时候我们还可以对源码进行翻译,再制作,效果还不错.今天介绍的A

.NET平台开源项目速览(3)小巧轻量级NoSQL文件数据库LiteDB

原文:.NET平台开源项目速览(3)小巧轻量级NoSQL文件数据库LiteDB     今天给大家介绍一个不错的小巧轻量级的NoSQL文件数据库LiteDB.本博客在2013年也介绍过2款.NET平台的开源数据库: 1.[原创]开源.NET下的XML数据库介绍及入门  2.[原创]C#开源轻量级对象数据库NDatabase介绍      上面2个数据库我的实际的项目中用过,还不错.当然数据量很小,主要是客户比较变态,必须要用xml文件保存,就想到了,另外NDatabase只是自己觉得好玩,也用了

.NET平台开源项目速览(7)关于NoSQL数据库LiteDB的分页查询解决过程

原文:.NET平台开源项目速览(7)关于NoSQL数据库LiteDB的分页查询解决过程 在文章:这些.NET开源项目你知道吗?让.NET开源来得更加猛烈些吧!(第二辑) 与 .NET平台开源项目速览(3)小巧轻量级NoSQL文件数据库LiteDB中,介绍了LiteDB的基本使用情况以及部分技术细节,我还没有在实际系统中大量使用,但文章发布后,有不少网友( loogn)反应在实际项目中使用过,效果还可以吧.同时也有人碰到了关于LiteDB关于分页的问题,还不止一个网友,很显然这个问题从我的思考上来

.NET平台开源项目速览(11)KwCombinatorics排列组合使用案例(1)

    今年上半年,我在KwCombinatorics系列文章中,重点介绍了KwCombinatorics组件的使用情况,其实这个组件我5年前就开始用了,非常方便,麻雀虽小五脏俱全.所以一直非常喜欢,才写了几篇文章推荐给大家.最近在计算足球彩票结果组合过程中,使用的到了其功能,生成排列,非常具有代表性,而且也有网友咨询过类似的问题,所以就封装为扩展方法,方便调用.         NET开源目录:[目录]本博客其他.NET开源项目文章目录 彩票数据资料目录:[目录]C#搭建足球赛事资料库与预测平

.NET平台开源项目速览(8)Expression Evaluator表达式计算组件使用

在文章:这些.NET开源项目你知道吗?让.NET开源来得更加猛烈些吧!(第二辑)中,给大家初步介绍了一下Expression Evaluator验证组件.那里只是概述了一下,并没有对其使用和强大功能做深入研究,所以今天就通过一篇简单的文章来预览一下其强大的功能.本文曾在[原创].NET开源表达式计算组件介绍与使用一文中介绍过一个.NET平台开源的表达式计算组件NCal.不过经过比较还是这个Expression Evaluator比较强大,虽然部分功能有重叠.也都能计算常规的数学表达式.这个组件的

.NET平台开源项目速览(12)哈希算法集合类库HashLib

    .NET的System.Security.Cryptography命名空间本身是提供加密服务,散列函数,对称与非对称加密算法等功能.实际上,大部分情况下已经满足了需求,而且.NET实现的都是目前国际上比较权威的,标准化的算法,所以还是安全可靠的.但也有一些场合,需要自己实现一些安全散列算法.不仅仅是学习,也可以进行测试以及相关研究.而今天要介绍的正式这样一个包括了目前几乎所有散列函数算法实现的.NET开源组件,大家可以实际使用,查看或者修改等.满足更多不同人,不同层次的需求.那就看看相关

[置顶].NET平台开源项目速览(6)FluentValidation验证组件介绍与入门(一)

    在文章:这些.NET开源项目你知道吗?让.NET开源来得更加猛烈些吧!(第二辑)中,给大家初步介绍了一下FluentValidation验证组件.那里只是概述了一下,并没有对其使用和强大功能做深入研究,所以今天以及接下去的几篇文章就专门介绍这个组件.不仅仅是它小,轻量级,优雅,而且一直在持续更新中.本人对这个感触很深是源于4年前自己在做一个数据过滤软件时,自己也设计了一套验证过滤的东西,虽然勉强能用,但太复杂了,复杂到我看到就想吐...指导我遇到了FluentValidation,彻底颠