C#使用Linq读写ini文件

前言:

用C#读写ini文件,网上千篇一律的都是去调用Windows API函数,这让强大的LINQ情何以堪。

现在配置文件已经流行使用XML格式,但对于界面语言,我还是偏爱使用ini文件来支持多国语言。

开发工具:

Visual Studio v2010

.NET Framework 4 Client Profile

版本历史:

V1.0    2011年06月30日

  • 基于LINQ实现对ini文件的读写。

下载地址:

 https://files.cnblogs.com/greatverve/LINQToINI.zip

url:http://greatverve.cnblogs.com/archive/2011/07/04/linq-ini.html
源代码:

LINQToINI.cs

/* ---------------------------------------------------------- 
  1. 文件名称:LINQToINI.cs 
  2.  
  3. 作者:秦建辉 
  4.  
  5. MSN:splashcn@msn.com 
  6. QQ:36748897 
  7.  
  8. 开发环境: 
  9.     Visual Studio V2010 
  10.     .NET Framework 4 Client Profile 
  11.  
  12. 版本历史: 
  13.     V1.0    2011年06月30日 
  14.             基于LINQ实现对ini文件的读写 
  15. ------------------------------------------------------------ */  
  16. using System;  
  17. using System.Collections.Generic;  
  18. using System.Linq;  
  19. using System.Text;  
  20. using System.IO;  
  21.   
  22. namespace Splash  
  23. {  
  24.     /// <summary>  
  25.     /// 基于LINQ实现对ini文件的读写  
  26.     /// </summary>  
  27.     public class LINQToINI  
  28.     {  
  29.         /// <summary>  
  30.         /// ini文件内容结点结构  
  31.         /// </summary>  
  32.         private struct ININode  
  33.         {  
  34.             /// <summary>  
  35.             /// 分区名  
  36.             /// </summary>  
  37.             public String section;  
  38.   
  39.             /// <summary>  
  40.             /// 键名  
  41.             /// </summary>  
  42.             public String keyName;  
  43.   
  44.             /// <summary>  
  45.             /// 键值  
  46.             /// </summary>  
  47.             public String keyValue;  
  48.         }  
  49.           
  50.         /// <summary>  
  51.         /// ini文件内容结点列表  
  52.         /// </summary>  
  53.         private List<ININode> INIElement = null;                      
  54.           
  55.         /// <summary>  
  56.         /// 读取ini文件并序列化,以供LINQ查询  
  57.         /// </summary>  
  58.         /// <param name="iniFile">ini文件名</param>  
  59.         /// <param name="isQueryOnly">是否只做查询操作。缺省为true</param>  
  60.         /// <param name="fileEncoding">文件编码,缺省为null,使用Unicode编码</param>  
  61.         /// <returns>  
  62.         ///     true:成功  
  63.         ///     false:失败  
  64.         /// </returns>       
  65.         /// <remarks>  
  66.         /// 如果只做查询操作,则序列化时去掉空行结点和注释行结点  
  67.         /// </remarks>  
  68.         public Boolean Load(String iniFile, Boolean isQueryOnly = true, Encoding fileEncoding = null)  
  69.         {  
  70.             if (fileEncoding == null)  
  71.             {   // 默认使用Unicode编码  
  72.                 fileEncoding = Encoding.Unicode;  
  73.             }  
  74.   
  75.             try  
  76.             {   // 自动检测文件编码  
  77.                 using (StreamReader sr = new StreamReader(iniFile, fileEncoding, true))  
  78.                 {  
  79.                     if (INIElement == null)  
  80.                     {  
  81.                         INIElement = new List<ININode>();  
  82.                     }  
  83.                     else  
  84.                     {  
  85.                         INIElement.Clear();  
  86.                     }  
  87.                       
  88.                     String Section = null;  
  89.                     while (true)  
  90.                     {  
  91.                         String Source = sr.ReadLine();  
  92.                         if (Source == nullbreak;  
  93.   
  94.                         Source = Source.Trim();  
  95.                         if (Source == String.Empty)  
  96.                         {   // 空行  
  97.                             if (!isQueryOnly)  
  98.                             {  
  99.                                 INIElement.Add(new ININode { section = "\u000A", keyName = null, keyValue = null });  
  100.                             }  
  101.                         }  
  102.                         else if (Source[0] == '#' || Source[0] == ';')  
  103.                         {   // 注释行  
  104.                             if (!isQueryOnly)  
  105.                             {  
  106.                                 INIElement.Add(new ININode { section = "\u000B", keyName = null, keyValue = Source });  
  107.                             }  
  108.                         }  
  109.                         else if (Source[0] == '[')  
  110.                         {   // 分区名  
  111.                             Int32 RightSquareBracketIndex = Source.IndexOf(']');  
  112.                             if (RightSquareBracketIndex != -1)  
  113.                             {  
  114.                                 Section = Source.Substring(1, RightSquareBracketIndex - 1).Trim();  
  115.                                 if (Section != String.Empty)  
  116.                                 {  
  117.                                     INIElement.Add(new ININode { section = Section, keyName = String.Empty, keyValue = null });  
  118.                                 }  
  119.                             }  
  120.                         }  
  121.                         else  
  122.                         {   // 键名键值对  
  123.                             if (!String.IsNullOrEmpty(Section))  
  124.                             {  
  125.                                 Int32 EqualsSignIndex = Source.IndexOf('=');  
  126.                                 if (EqualsSignIndex != -1)  
  127.                                 {   // 提取键名  
  128.                                     String KeyName = Source.Substring(0, EqualsSignIndex).Trim();  
  129.                                     if (KeyName != String.Empty)  
  130.                                     {   // 提取键值  
  131.                                         String KeyValue = Source.Substring(EqualsSignIndex + 1).Trim();  
  132.                                         if(KeyValue.Length >= 2)  
  133.                                         {   // 判断是否有双引号  
  134.                                             if (KeyValue[0] == '\u0022' && KeyValue[KeyValue.Length - 1] == '\u0022')  
  135.                                             {     
  136.                                                 KeyValue = KeyValue.Substring(1, KeyValue.Length - 2);  
  137.                                             }  
  138.                                         }  
  139.   
  140.                                         INIElement.Add(new ININode { section = Section, keyName = KeyName, keyValue = KeyValue });  
  141.                                     }  
  142.                                 }  
  143.                             }  
  144.                         }  
  145.                     }  
  146.   
  147.                     sr.Close();  
  148.   
  149.                     return true;  
  150.                 }  
  151.             }  
  152.   
  153.             catch (Exception)  
  154.             {  
  155.                 return false;  
  156.             }              
  157.         }  
  158.   
  159.         /// <summary>  
  160.         /// 存储ini文件  
  161.         /// </summary>  
  162.         /// <param name="iniFile">要存储的文件名</param>  
  163.         /// <param name="fileEncoding">文件编码。缺省为null,使用Unicode编码</param>  
  164.         /// <returns>  
  165.         ///     true:成功  
  166.         ///     false:失败  
  167.         /// </returns>  
  168.         /// <remarks>注意:只有调用此函数,才能保存最终数据</remarks>  
  169.         public Boolean Save(String iniFile, Encoding fileEncoding = null)  
  170.         {  
  171.             if (INIElement == null)  
  172.             {   // 抛出异常:无效的数据源  
  173.                 throw new ApplicationException("Invalid Data Source!");  
  174.             }  
  175.               
  176.             if (fileEncoding == null)  
  177.             {   // 默认使用Unicode编码  
  178.                 fileEncoding = Encoding.Unicode;  
  179.             }  
  180.   
  181.             try  
  182.             {                  
  183.                 using (StreamWriter sw = new StreamWriter(iniFile, false, fileEncoding))  
  184.                 {  
  185.                     foreach (ININode node in INIElement)  
  186.                     {  
  187.                         if (node.keyName == null)  
  188.                         {  
  189.                             if (node.section == "\u000A")  
  190.                             {   // 空行  
  191.                                 sw.WriteLine();  
  192.                             }  
  193.                             else if (node.section == "\u000B")  
  194.                             {   // 注释行  
  195.                                 sw.WriteLine(node.keyValue);  
  196.                             }  
  197.                         }  
  198.                         else  
  199.                         {  
  200.                             if (node.keyName == String.Empty)  
  201.                             {   // 分区  
  202.                                 sw.WriteLine("[" + node.section + "]");  
  203.                             }  
  204.                             else  
  205.                             {   // 键名键值对  
  206.                                 if (node.keyValue.IndexOf(' ') == -1)  
  207.                                 {   // 键值中没有空格  
  208.                                     sw.WriteLine(node.keyName + "=" + node.keyValue);  
  209.                                 }  
  210.                                 else  
  211.                                 {   // 键值中包含空格,需在键值两端加上引号  
  212.                                     sw.WriteLine(node.keyName + "=\u0022" + node.keyValue + "\u0022");  
  213.                                 }  
  214.                             }  
  215.                         }  
  216.                     }  
  217.   
  218.                     sw.Close();  
  219.                     return true;  
  220.                 }  
  221.             }  
  222.   
  223.             catch (Exception)  
  224.             {  
  225.                 return false;  
  226.             }  
  227.         }  
  228.   
  229.         /// <summary>  
  230.         /// 提取键名对应的键值  
  231.         /// </summary>  
  232.         /// <param name="section">分区名。如果为null,则提取所有的分区名</param>  
  233.         /// <param name="keyName">键名。如果为null,则提取分区所有的键名键值对</param>  
  234.         /// <param name="defaultString">缺省键值</param>  
  235.         /// <returns>键值</returns>  
  236.         public String[] GetProfileString(String section, String keyName, String defaultString)  
  237.         {  
  238.             if (INIElement == null)  
  239.             {   // 抛出异常:无效的数据源  
  240.                 throw new ApplicationException("Invalid Data Source!");  
  241.             }  
  242.   
  243.             if(section == null)  
  244.             {   // 获取所有的分区名  
  245.                 return (from node in INIElement where (node.keyName == String.Empty) select node.section).ToArray();  
  246.             }  
  247.             else if (keyName == null)  
  248.             {   // 获取指定分区所有的键名及键值  
  249.                 return (from node in INIElement where (String.Compare(node.section, section, true) == 0 && !String.IsNullOrEmpty(node.keyName)) select (node.keyName + "=" + node.keyValue)).ToArray();  
  250.             }  
  251.             else  
  252.             {   // 获取键值  
  253.                 var ValueQuery = (from node in INIElement where (String.Compare(node.section, section, true) == 0 && String.Compare(node.keyName, keyName, true) == 0) select node.keyValue).ToArray();  
  254.                 if (ValueQuery.Length == 0)  
  255.                 {  
  256.                     return new String[] { defaultString };  
  257.                 }  
  258.                 else  
  259.                 {  
  260.                     return ValueQuery;  
  261.                 }  
  262.             }  
  263.         }  
  264.   
  265.         /// <summary>  
  266.         /// 提取键名对应的键值(整数值)  
  267.         /// </summary>  
  268.         /// <param name="section">分区名</param>  
  269.         /// <param name="keyName">键名</param>  
  270.         /// <param name="defaultValue">缺省键值</param>  
  271.         /// <returns>键值(整数值)</returns>  
  272.         public Int32 GetProfileInt(String section, String keyName, Int32 defaultValue)  
  273.         {  
  274.             if (INIElement == null)  
  275.             {   // 抛出异常:无效的数据源  
  276.                 throw new ApplicationException("Invalid Data Source!");  
  277.             }  
  278.   
  279.             if (String.IsNullOrEmpty(section) || String.IsNullOrEmpty(keyName))  
  280.             {  
  281.                 return defaultValue;  
  282.             }  
  283.   
  284.             // 获取键值  
  285.             var ValueQuery = (from node in INIElement where (String.Compare(node.section, section, true) == 0 && String.Compare(node.keyName, keyName, true) == 0) select node.keyValue).ToArray();  
  286.             if (ValueQuery.Length == 0)  
  287.             {  
  288.                 return defaultValue;  
  289.             }  
  290.             else  
  291.             {  
  292.                 if (ValueQuery[0] == String.Empty)  
  293.                 {  
  294.                     return defaultValue;  
  295.                 }  
  296.                 else  
  297.                 {   // 将字符串转换为整数值(注意:可能会抛出异常)  
  298.                     return Convert.ToInt32(ValueQuery[0]);  
  299.                 }  
  300.             }  
  301.         }  
  302.   
  303.         /// <summary>  
  304.         /// 获取分区所有的键名键值对  
  305.         /// </summary>  
  306.         /// <param name="section">分区名</param>  
  307.         /// <returns>键名键值对数组</returns>  
  308.         public String[] GetProfileSection(String section)  
  309.         {  
  310.             if (INIElement == null)  
  311.             {   // 抛出异常:无效的数据源  
  312.                 throw new ApplicationException("Invalid Data Source!");  
  313.             }  
  314.   
  315.             if (String.IsNullOrEmpty(section))  
  316.             {  
  317.                 return null;  
  318.             }  
  319.             else  
  320.             {   // 获取指定分区所有的键名及键值  
  321.                 return (from node in INIElement where (String.Compare(node.section, section, true) == 0 && !String.IsNullOrEmpty(node.keyName)) select (node.keyName + "=" + node.keyValue)).ToArray();  
  322.             }  
  323.         }  
  324.   
  325.         /// <summary>  
  326.         /// 获取所有的分区名  
  327.         /// </summary>  
  328.         /// <returns>分区名数组</returns>  
  329.         public String[] GetProfileSectionNames()  
  330.         {  
  331.             if (INIElement == null)  
  332.             {   // 抛出异常:无效的数据源  
  333.                 throw new ApplicationException("Invalid Data Source!");  
  334.             }  
  335.   
  336.             // 获取所有的分区名  
  337.             return (from node in INIElement where (String.Compare(node.keyName, String.Empty, true) == 0) select node.section).ToArray();  
  338.         }  
  339.   
  340.         /// <summary>  
  341.         /// 增加或更新分区名、键名或者键值  
  342.         /// </summary>  
  343.         /// <param name="section">分区名</param>  
  344.         /// <param name="keyName">键名。如果为null或者空串,则删除整个分区</param>  
  345.         /// <param name="keyValue">键值。如果为null,则删除该键</param>  
  346.         /// <returns>  
  347.         ///     true:成功  
  348.         ///     false:失败  
  349.         /// </returns>  
  350.         public Boolean WriteProfileString(String section, String keyName, String keyValue)  
  351.         {  
  352.             if (String.IsNullOrEmpty(section))  
  353.             {  
  354.                 return false;  
  355.             }  
  356.   
  357.             if (INIElement == null)  
  358.             {   // 初始化ini结点列表  
  359.                 INIElement = new List<ININode>();  
  360.             }  
  361.   
  362.             if (String.IsNullOrEmpty(keyName))  
  363.             {   // 删除整个分区(关键:要从后往前删)  
  364.                 for (Int32 i = INIElement.Count - 1; i >= 0; i--)  
  365.                 {  
  366.                     if (String.Compare(INIElement[i].section, section, true) == 0 && INIElement[i].keyName != null)  
  367.                     {  
  368.                         INIElement.RemoveAt(i);  
  369.                     }  
  370.                 }  
  371.             }  
  372.             else  
  373.             {   // 更新键值  
  374.                 Int32 InsertIndex = -1;  
  375.                 for (Int32 i = INIElement.Count - 1; i >= 0; i--)  
  376.                 {  
  377.                     if (String.Compare(INIElement[i].section, section, true) == 0)  
  378.                     {         
  379.                         if (String.Compare(INIElement[i].keyName, keyName, true) == 0)  
  380.                         {   // 删除该键  
  381.                             INIElement.RemoveAt(i);  
  382.                             if (keyValue != null)  
  383.                             {   // 更新该键  
  384.                                 if (keyValue.Length >= 2)  
  385.                                 {   // 判断是否有双引号  
  386.                                     if (keyValue[0] == '\u0022' && keyValue[keyValue.Length - 1] == '\u0022')  
  387.                                     {  
  388.                                         keyValue = keyValue.Substring(1, keyValue.Length - 2);  
  389.                                     }  
  390.                                 }  
  391.   
  392.                                 // 插入更新后的键名键值对  
  393.                                 INIElement.Insert(i, new ININode { section = section, keyName = keyName, keyValue = keyValue });  
  394.                             }  
  395.   
  396.                             // 直接返回  
  397.                             return true;  
  398.                         }  
  399.   
  400.                         if (InsertIndex == -1)  
  401.                         {   // 将分区末尾做为插入点  
  402.                             InsertIndex = i + 1;  
  403.                         }  
  404.                     }  
  405.                 }  
  406.   
  407.                 if (InsertIndex == -1)  
  408.                 {   // 分区不存在,首先增加新的分区名  
  409.                     INIElement.Add(new ININode { section = section, keyName = String.Empty, keyValue = null });  
  410.                       
  411.                     // 再增加新的键名键值对  
  412.                     INIElement.Add(new ININode { section = section, keyName = keyName, keyValue = keyValue });  
  413.                 }  
  414.                 else  
  415.                 {   // 分区存在,在分区末尾增加新的键名键值对  
  416.                     INIElement.Insert(InsertIndex, new ININode { section = section, keyName = keyName, keyValue = keyValue });  
  417.                 }  
  418.             }  
  419.   
  420.             return true;  
  421.         }  
  422.   
  423.         /// <summary>  
  424.         /// 替换分区的键名键值对  
  425.         /// </summary>  
  426.         /// <param name="section">分区名</param>  
  427.         /// <param name="keyValueSet">要替换的键名键值对。如果为null,则删除整个分区</param>  
  428.         /// <returns>  
  429.         ///     true:成功  
  430.         ///     false:失败  
  431.         /// </returns>  
  432.         public Boolean WriteProfileSection(String section, String[] keyValueSet)  
  433.         {  
  434.             if (String.IsNullOrEmpty(section))  
  435.             {  
  436.                 return false;  
  437.             }  
  438.   
  439.             if (INIElement == null)  
  440.             {   // 初始化ini结点列表  
  441.                 INIElement = new List<ININode>();  
  442.             }  
  443.   
  444.             // 删除整个分区  
  445.             Int32 InsertIndex = INIElement.Count;  
  446.             for (Int32 i = INIElement.Count - 1; i >= 0; i--)  
  447.             {  
  448.                 if (String.Compare(INIElement[i].section, section, true) == 0 && INIElement[i].keyName != null)  
  449.                 {  
  450.                     INIElement.RemoveAt(i);  
  451.                     InsertIndex = i;  
  452.                 }  
  453.             }        
  454.   
  455.             if (keyValueSet != null)  
  456.             {   // 写入分区名  
  457.                 INIElement.Insert(InsertIndex++, new ININode {section = section, keyName = String.Empty, keyValue = null });  
  458.                   
  459.                 // 写入键名键值对  
  460.                 foreach (String s in keyValueSet)  
  461.                 {  
  462.                     Int32 EqualsSignIndex = s.IndexOf('=');  
  463.                     if (EqualsSignIndex != -1)  
  464.                     {  
  465.                         String KeyName = s.Substring(0, EqualsSignIndex).Trim();  
  466.                         if (KeyName != String.Empty)  
  467.                         {  
  468.                             String KeyValue = s.Substring(EqualsSignIndex + 1).Trim();  
  469.                             if (KeyValue.Length >= 2)  
  470.                             {   // 判断是否有双引号  
  471.                                 if (KeyValue[0] == '\u0022' && KeyValue[KeyValue.Length - 1] == '\u0022')  
  472.                                 {  
  473.                                     KeyValue = KeyValue.Substring(1, KeyValue.Length - 2);  
  474.                                 }  
  475.                             }  
  476.   
  477.                             INIElement.Insert(InsertIndex++, new ININode { section = section, keyName = KeyName, keyValue = KeyValue });  
  478.                         }  
  479.                     }  
  480.                 }             
  481.             }  
  482.   
  483.             return true;  
  484.         }  
  485.   
  486.         /// <summary>  
  487.         /// 将结构数据写入键值  
  488.         /// </summary>  
  489.         /// <param name="section">分区名</param>  
  490.         /// <param name="keyName">键名</param>  
  491.         /// <param name="data">数据</param>  
  492.         /// <returns>  
  493.         ///     true:成功  
  494.         ///     false:失败  
  495.         /// </returns>  
  496.         public Boolean WriteProfileStruct(String section, String keyName, Byte[] data)  
  497.         {  
  498.             if (String.IsNullOrEmpty(section) || String.IsNullOrEmpty(keyName))  
  499.             {  
  500.                 return false;  
  501.             }  
  502.   
  503.             if (data == null)  
  504.             {  
  505.                 return WriteProfileString(section, keyName, null);  
  506.             }  
  507.   
  508.             // 将字节数组转换成16进制字符串  
  509.             StringBuilder sb = new StringBuilder((data.Length + 1) << 1);  
  510.             Int32 CheckSum = 0;  
  511.             foreach(Byte b in data)  
  512.             {  
  513.                 CheckSum += b;  
  514.                 sb.Append(b.ToString("X2"));  
  515.             }  
  516.   
  517.             // 写入校验和  
  518.             sb.Append(((Byte)CheckSum).ToString("X2"));  
  519.   
  520.             return WriteProfileString(section, keyName, sb.ToString());  
  521.         }  
  522.   
  523.         /// <summary>  
  524.         /// 提取键值,并转化为字节数组  
  525.         /// </summary>  
  526.         /// <param name="section">分区名</param>  
  527.         /// <param name="keyName">键名</param>  
  528.         /// <returns>键值对应的字节数组</returns>  
  529.         public Byte[] GetProfileStruct(String section, String keyName)  
  530.         {  
  531.             if (INIElement == null)  
  532.             {   // 抛出异常:无效的数据源  
  533.                 throw new ApplicationException("Invalid Data Source!");  
  534.             }  
  535.   
  536.             if (String.IsNullOrEmpty(section) || String.IsNullOrEmpty(keyName))  
  537.             {  
  538.                 return null;  
  539.             }  
  540.   
  541.             // 获取键值  
  542.             var ValueQuery = (from node in INIElement where (String.Compare(node.section, section, true) == 0 && String.Compare(node.keyName, keyName, true) == 0) select node.keyValue).ToArray();  
  543.             if (ValueQuery.Length != 1)  
  544.             {  
  545.                 return null;  
  546.             }  
  547.   
  548.             // 将16进制字符串转换成字节数组  
  549.             String s = ValueQuery[0];  
  550.             if (String.IsNullOrEmpty(s) || (s.Length % 2 != 0))  
  551.             {  
  552.                 return null;  
  553.             }  
  554.   
  555.             try  
  556.             {  
  557.                 Int32 Num = s.Length / 2 - 1;  
  558.                 Byte[] ValueArray = new Byte[Num];  
  559.                 Int32 CheckSum = 0;  
  560.                 for (Int32 i = 0; i < Num; i++)  
  561.                 {  
  562.                     CheckSum += ValueArray[i] = Convert.ToByte(s.Substring(i << 1, 2), 16);  
  563.                 }  
  564.   
  565.                 // 检测校验和  
  566.                 if (Convert.ToByte(s.Substring(Num << 1, 2), 16) == CheckSum)  
  567.                 {  
  568.                     return ValueArray;  
  569.                 }  
  570.                 else  
  571.                 {   // 校验失败  
  572.                     return null;  
  573.                 }  
  574.             }  
  575.   
  576.             catch (Exception)  
  577.             {   // 无效字符串  
  578.                 return null;  
  579.             }  
  580.         }  
  581.     }  
  582. }  
posted @ 2011-07-04 10:17  大气象  阅读(2175)  评论(4编辑  收藏  举报
http://www.tianqiweiqi.com