.NET平台開源項目速覽(5)深入使用與擴展SharpConfig組件


  上個月在文章:這些.NET開源項目你知道嗎?讓.NET開源來得更加猛烈些吧  和 .NET平台開源項目速覽(1)SharpConfig配置文件讀寫組件 中都提到了SharpConfig組件,簡單輕量級,速度快,而且還有比較深入的使用介紹。在文章發布后,也有網友提到一些問題,當時我也沒仔細去分析,在這次我親自使用的過程中,就對幾個問題進行了比較深入的研究,同時對不滿足自己的地方,也進行了擴展。所以今天就把對SharpConfig的源碼進行一個簡單的分析,同時也根據需求對自己的一個特殊情況進行擴展。自己動手豐衣足食。。。

.NET開源目錄:【目錄】本博客其他.NET開源項目文章目錄

 本文原文地址:.NET平台開源項目速覽(5)深入使用與擴展SharpConfig組件

1.SharpConfig源碼分析

  SharpConfig源碼並不大,核心代碼其實也很簡單,就是文件讀寫,解析。在深入使用和擴展SharpConfig之前,有必要了解一下它的基本結構。所以先來介紹SharpConfig源碼中核心的3大類。

   Configuration是核心類,我們在前面的文章中只是簡單的介紹了一下如何加載配置文件,查看源代碼,可以發現加載和保存的方法都是匹配的,都可以從文件或者數據流中加載或者保存。

blob.png

     由於每一個配置文件都包含若干個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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM