背景
之前采用ini文件作为程序的配置文件,觉得这种结构简单明了,配置起来也挺方便。然后操作方式是通过WindowsAPI,然后再网上找到一个基于WindowsAPI封装的help类,用起来倒也顺手。用的多了,觉得还可以使用文件流来操作,后来就发现了EasyConfig。
EasyConfig是一个纯C#的开源ini文件操作库,但是在使用上有诸多不便,只是个人感觉,于是个人将其改造了一下,是自己喜欢用的风格。
资源下载
如果不清楚ini文件结构,请百度一下,这里贴一个示例文件。
[Video] #是否全屏 Fullscreen = true #宽度 Width = 1280 #电视高度 #高度 Height = 720 [Level1] NumberOfEnemies=2000#数值 Lives = 10 Timer = 999 EnemyNames = "Steve", "Sam", "Bill" EnemyGuns = 13, 28, 43, 499 CanShoot = true, yes, on, no, false, off
代码示例
首先来看看改写后,怎么去使用她来读写Ini文件。
static void Main(string[] args) { ConfigFile configFile = new ConfigFile("Test.ini"); //遍历Config文件 foreach (var group in configFile.SettingGroups) { Console.WriteLine("****************************"); Console.WriteLine(group.Key + ":"); Console.WriteLine(); foreach (var value in group.Value.Settings) Console.WriteLine("{0} = {1} (Is Array? {2}),{3}", value.Key, value.Value.RawValue, value.Value.IsArray, value.Value.Desp); Console.WriteLine(); } //读取值主要是在具体的配置项上进行读取,是首先定位到[Group],其次就是具体的项 //使用泛型读取值,并指定读取失败时的默认值 var fullScreen = configFile["Video"]["Fullscreen"].As<int>(-1);//失败 var bFullScreen = configFile["Video"]["Fullscreen"].AsBool();//成功 var arrCanShoot = configFile["Level1"]["CanShoot"].AsArray<bool>(); //读取时 该项不存在,不存在的组或者项会自动添加 var noexists = configFile["Video"]["xxxxxxxxxxx"].AsString(); var noexists2 = configFile["Video111111111"]["xxxxxxxxxxx"].AsString(); //写入值有2种方法,可以直接向某组下写入一项,也可以定位到某组某项,写入值 //写入值 该项不存在,组或者项会自动创建 configFile["Video"].WriteSetting("NewName", "EasyConfig"); configFile["Video"].WriteSetting("NewName", "EasyConfig2.0"); configFile["Video22222222"].WriteSetting("c1", "1"); //索引器不过是返回Setting configFile["Video3333"]["UserName"].SetValue("admin"); configFile["Viedo4444"]["Sex"].SetValue("男", "女", "保密"); //写入值,该项不存在 configFile["Video222"].WriteSetting("NewName", "EasyConfig3.0"); Console.ReadKey(true); configFile.Save("TestConfig2.txt"); }
基本上自己想要的效果,则是基于 ConfigFile["GroupName"]["Key"] 这样的方式来进行读写。
另外附上一个基于WindowsAPI操作的封装类。
1 /// <summary> 2 /// Provides methods for reading and writing to an INI file. 3 /// </summary> 4 public class IniFileHelp 5 { 6 /// <summary> 7 /// The maximum size of a section in an ini file. 8 /// </summary> 9 /// <remarks> 10 /// This property defines the maximum size of the buffers 11 /// used to retreive data from an ini file. This value is 12 /// the maximum allowed by the win32 functions 13 /// GetPrivateProfileSectionNames() or 14 /// GetPrivateProfileString(). 15 /// </remarks> 16 public const int MaxSectionSize = 32767; // 32 KB 17 18 //The path of the file we are operating on. 19 private string m_path; 20 21 #region P/Invoke declares 22 23 /// <summary> 24 /// A static class that provides the win32 P/Invoke signatures 25 /// used by this class. 26 /// </summary> 27 /// <remarks> 28 /// Note: In each of the declarations below, we explicitly set CharSet to 29 /// Auto. By default in C#, CharSet is set to Ansi, which reduces 30 /// performance on windows 2000 and above due to needing to convert strings 31 /// from Unicode (the native format for all .Net strings) to Ansi before 32 /// marshalling. Using Auto lets the marshaller select the Unicode version of 33 /// these functions when available. 34 /// </remarks> 35 [System.Security.SuppressUnmanagedCodeSecurity] 36 private static class NativeMethods 37 { 38 [DllImport("kernel32.dll", CharSet = CharSet.Auto)] 39 public static extern int GetPrivateProfileSectionNames(IntPtr lpszReturnBuffer, 40 uint nSize, 41 string lpFileName); 42 43 [DllImport("kernel32.dll", CharSet = CharSet.Auto)] 44 public static extern uint GetPrivateProfileString(string lpAppName, 45 string lpKeyName, 46 string lpDefault, 47 StringBuilder lpReturnedString, 48 int nSize, 49 string lpFileName); 50 51 [DllImport("kernel32.dll", CharSet = CharSet.Auto)] 52 public static extern uint GetPrivateProfileString(string lpAppName, 53 string lpKeyName, 54 string lpDefault, 55 [In, Out] char[] lpReturnedString, 56 int nSize, 57 string lpFileName); 58 59 [DllImport("kernel32.dll", CharSet = CharSet.Auto)] 60 public static extern int GetPrivateProfileString(string lpAppName, 61 string lpKeyName, 62 string lpDefault, 63 IntPtr lpReturnedString, 64 uint nSize, 65 string lpFileName); 66 67 [DllImport("kernel32.dll", CharSet = CharSet.Auto)] 68 public static extern int GetPrivateProfileInt(string lpAppName, 69 string lpKeyName, 70 int lpDefault, 71 string lpFileName); 72 73 [DllImport("kernel32.dll", CharSet = CharSet.Auto)] 74 public static extern int GetPrivateProfileSection(string lpAppName, 75 IntPtr lpReturnedString, 76 uint nSize, 77 string lpFileName); 78 79 //We explicitly enable the SetLastError attribute here because 80 // WritePrivateProfileString returns errors via SetLastError. 81 // Failure to set this can result in errors being lost during 82 // the marshal back to managed code. 83 [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] 84 public static extern bool WritePrivateProfileString(string lpAppName, 85 string lpKeyName, 86 string lpString, 87 string lpFileName); 88 89 90 } 91 #endregion 92 93 /// <summary> 94 /// Initializes a new instance of the <see cref="IniFileHelp"/> class. 95 /// </summary> 96 /// <param name="path">The ini file to read and write from.</param> 97 public IniFileHelp(string path) 98 { 99 //Convert to the full path. Because of backward compatibility, 100 // the win32 functions tend to assume the path should be the 101 // root Windows directory if it is not specified. By calling 102 // GetFullPath, we make sure we are always passing the full path 103 // the win32 functions. 104 m_path = System.IO.Path.GetFullPath(path); 105 } 106 107 /// <summary> 108 /// Gets the full path of ini file this object instance is operating on. 109 /// </summary> 110 /// <value>A file path.</value> 111 public string Path 112 { 113 get 114 { 115 return m_path; 116 } 117 } 118 119 #region Get Value Methods 120 121 /// <summary> 122 /// Gets the value of a setting in an ini file as a <see cref="T:System.String"/>. 123 /// </summary> 124 /// <param name="sectionName">The name of the section to read from.</param> 125 /// <param name="keyName">The name of the key in section to read.</param> 126 /// <param name="defaultValue">The default value to return if the key 127 /// cannot be found.</param> 128 /// <returns>The value of the key, if found. Otherwise, returns 129 /// <paramref name="defaultValue"/></returns> 130 /// <remarks> 131 /// The retreived value must be less than 32KB in length. 132 /// </remarks> 133 /// <exception cref="ArgumentNullException"> 134 /// <paramref name="sectionName"/> or <paramref name="keyName"/> are 135 /// a null reference (Nothing in VB) 136 /// </exception> 137 public string GetString(string sectionName, 138 string keyName, 139 string defaultValue) 140 { 141 if (sectionName == null) 142 throw new ArgumentNullException("sectionName"); 143 144 if (keyName == null) 145 throw new ArgumentNullException("keyName"); 146 147 StringBuilder retval = new StringBuilder(IniFileHelp.MaxSectionSize); 148 149 NativeMethods.GetPrivateProfileString(sectionName, 150 keyName, 151 defaultValue, 152 retval, 153 IniFileHelp.MaxSectionSize, 154 m_path); 155 156 return retval.ToString(); 157 } 158 159 /// <summary> 160 /// Gets the value of a setting in an ini file as a <see cref="T:System.Int16"/>. 161 /// </summary> 162 /// <param name="sectionName">The name of the section to read from.</param> 163 /// <param name="keyName">The name of the key in section to read.</param> 164 /// <param name="defaultValue">The default value to return if the key 165 /// cannot be found.</param> 166 /// <returns>The value of the key, if found. Otherwise, returns 167 /// <paramref name="defaultValue"/>.</returns> 168 /// <exception cref="ArgumentNullException"> 169 /// <paramref name="sectionName"/> or <paramref name="keyName"/> are 170 /// a null reference (Nothing in VB) 171 /// </exception> 172 public int GetInt16(string sectionName, 173 string keyName, 174 short defaultValue) 175 { 176 int retval = GetInt32(sectionName, keyName, defaultValue); 177 178 return Convert.ToInt16(retval); 179 } 180 181 /// <summary> 182 /// Gets the value of a setting in an ini file as a <see cref="T:System.Int32"/>. 183 /// </summary> 184 /// <param name="sectionName">The name of the section to read from.</param> 185 /// <param name="keyName">The name of the key in section to read.</param> 186 /// <param name="defaultValue">The default value to return if the key 187 /// cannot be found.</param> 188 /// <returns>The value of the key, if found. Otherwise, returns 189 /// <paramref name="defaultValue"/></returns> 190 /// <exception cref="ArgumentNullException"> 191 /// <paramref name="sectionName"/> or <paramref name="keyName"/> are 192 /// a null reference (Nothing in VB) 193 /// </exception> 194 public int GetInt32(string sectionName, 195 string keyName, 196 int defaultValue) 197 { 198 if (sectionName == null) 199 throw new ArgumentNullException("sectionName"); 200 201 if (keyName == null) 202 throw new ArgumentNullException("keyName"); 203 204 205 return NativeMethods.GetPrivateProfileInt(sectionName, keyName, defaultValue, m_path); 206 } 207 208 /// <summary> 209 /// Gets the value of a setting in an ini file as a <see cref="T:System.Double"/>. 210 /// </summary> 211 /// <param name="sectionName">The name of the section to read from.</param> 212 /// <param name="keyName">The name of the key in section to read.</param> 213 /// <param name="defaultValue">The default value to return if the key 214 /// cannot be found.</param> 215 /// <returns>The value of the key, if found. Otherwise, returns 216 /// <paramref name="defaultValue"/></returns> 217 /// <exception cref="ArgumentNullException"> 218 /// <paramref name="sectionName"/> or <paramref name="keyName"/> are 219 /// a null reference (Nothing in VB) 220 /// </exception> 221 public double GetDouble(string sectionName, 222 string keyName, 223 double defaultValue) 224 { 225 string retval = GetString(sectionName, keyName, ""); 226 227 if (retval == null || retval.Length == 0) 228 { 229 return defaultValue; 230 } 231 232 return Convert.ToDouble(retval, CultureInfo.InvariantCulture); 233 } 234 235 #endregion 236 237 #region GetSectionValues Methods 238 239 /// <summary> 240 /// Gets all of the values in a section as a list. 241 /// </summary> 242 /// <param name="sectionName"> 243 /// Name of the section to retrieve values from. 244 /// </param> 245 /// <returns> 246 /// A <see cref="List{T}"/> containing <see cref="KeyValuePair{T1, T2}"/> objects 247 /// that describe this section. Use this verison if a section may contain 248 /// multiple items with the same key value. If you know that a section 249 /// cannot contain multiple values with the same key name or you don't 250 /// care about the duplicates, use the more convenient 251 /// <see cref="GetSectionValues"/> function. 252 /// </returns> 253 /// <exception cref="ArgumentNullException"> 254 /// <paramref name="sectionName"/> is a null reference (Nothing in VB) 255 /// </exception> 256 public List<KeyValuePair<string, string>> GetSectionValuesAsList(string sectionName) 257 { 258 List<KeyValuePair<string, string>> retval; 259 string[] keyValuePairs; 260 string key, value; 261 int equalSignPos; 262 263 if (sectionName == null) 264 throw new ArgumentNullException("sectionName"); 265 266 //Allocate a buffer for the returned section names. 267 IntPtr ptr = Marshal.AllocCoTaskMem(IniFileHelp.MaxSectionSize); 268 269 try 270 { 271 //Get the section key/value pairs into the buffer. 272 int len = NativeMethods.GetPrivateProfileSection(sectionName, 273 ptr, 274 IniFileHelp.MaxSectionSize, 275 m_path); 276 277 keyValuePairs = ConvertNullSeperatedStringToStringArray(ptr, len); 278 } 279 finally 280 { 281 //Free the buffer 282 Marshal.FreeCoTaskMem(ptr); 283 } 284 285 //Parse keyValue pairs and add them to the list. 286 retval = new List<KeyValuePair<string, string>>(keyValuePairs.Length); 287 288 for (int i = 0; i < keyValuePairs.Length; ++i) 289 { 290 //Parse the "key=value" string into its constituent parts 291 //Cancel a string start with '#' 292 var item = keyValuePairs[i].Trim(); 293 if (item.Length > 0 && !item.StartsWith("#")) 294 { 295 equalSignPos = keyValuePairs[i].IndexOf('='); 296 key = keyValuePairs[i].Substring(0, equalSignPos); 297 298 value = keyValuePairs[i].Substring(equalSignPos + 1, 299 keyValuePairs[i].Length - equalSignPos - 1); 300 301 retval.Add(new KeyValuePair<string, string>(key, value)); 302 } 303 } 304 305 return retval; 306 } 307 308 /// <summary> 309 /// Gets all of the values in a section as a dictionary. 310 /// </summary> 311 /// <param name="sectionName"> 312 /// Name of the section to retrieve values from. 313 /// </param> 314 /// <returns> 315 /// A <see cref="Dictionary{T, T}"/> containing the key/value 316 /// pairs found in this section. 317 /// </returns> 318 /// <remarks> 319 /// If a section contains more than one key with the same name, 320 /// this function only returns the first instance. If you need to 321 /// get all key/value pairs within a section even when keys have the 322 /// same name, use <see cref="GetSectionValuesAsList"/>. 323 /// </remarks> 324 /// <exception cref="ArgumentNullException"> 325 /// <paramref name="sectionName"/> is a null reference (Nothing in VB) 326 /// </exception> 327 public Dictionary<string, string> GetSectionValues(string sectionName) 328 { 329 List<KeyValuePair<string, string>> keyValuePairs; 330 Dictionary<string, string> retval; 331 332 keyValuePairs = GetSectionValuesAsList(sectionName); 333 334 //Convert list into a dictionary. 335 retval = new Dictionary<string, string>(keyValuePairs.Count); 336 337 foreach (KeyValuePair<string, string> keyValuePair in keyValuePairs) 338 { 339 //Skip any key we have already seen. 340 if (!retval.ContainsKey(keyValuePair.Key)) 341 { 342 retval.Add(keyValuePair.Key, keyValuePair.Value); 343 } 344 } 345 346 return retval; 347 } 348 349 #endregion 350 351 #region Get Key/Section Names 352 353 /// <summary> 354 /// Gets the names of all keys under a specific section in the ini file. 355 /// </summary> 356 /// <param name="sectionName"> 357 /// The name of the section to read key names from. 358 /// </param> 359 /// <returns>An array of key names.</returns> 360 /// <remarks> 361 /// The total length of all key names in the section must be 362 /// less than 32KB in length. 363 /// </remarks> 364 /// <exception cref="ArgumentNullException"> 365 /// <paramref name="sectionName"/> is a null reference (Nothing in VB) 366 /// </exception> 367 public string[] GetKeyNames(string sectionName) 368 { 369 int len; 370 string[] retval; 371 372 if (sectionName == null) 373 throw new ArgumentNullException("sectionName"); 374 375 //Allocate a buffer for the returned section names. 376 IntPtr ptr = Marshal.AllocCoTaskMem(IniFileHelp.MaxSectionSize); 377 378 try 379 { 380 //Get the section names into the buffer. 381 len = NativeMethods.GetPrivateProfileString(sectionName, 382 null, 383 null, 384 ptr, 385 IniFileHelp.MaxSectionSize, 386 m_path); 387 388 retval = ConvertNullSeperatedStringToStringArray(ptr, len); 389 } 390 finally 391 { 392 //Free the buffer 393 Marshal.FreeCoTaskMem(ptr); 394 } 395 396 return retval; 397 } 398 399 /// <summary> 400 /// Gets the names of all sections in the ini file. 401 /// </summary> 402 /// <returns>An array of section names.</returns> 403 /// <remarks> 404 /// The total length of all section names in the section must be 405 /// less than 32KB in length. 406 /// </remarks> 407 public string[] GetSectionNames() 408 { 409 string[] retval; 410 int len; 411 412 //Allocate a buffer for the returned section names. 413 IntPtr ptr = Marshal.AllocCoTaskMem(IniFileHelp.MaxSectionSize); 414 415 try 416 { 417 //Get the section names into the buffer. 418 len = NativeMethods.GetPrivateProfileSectionNames(ptr, 419 IniFileHelp.MaxSectionSize, m_path); 420 421 retval = ConvertNullSeperatedStringToStringArray(ptr, len); 422 } 423 finally 424 { 425 //Free the buffer 426 Marshal.FreeCoTaskMem(ptr); 427 } 428 429 return retval; 430 } 431 432 /// <summary> 433 /// Converts the null seperated pointer to a string into a string array. 434 /// </summary> 435 /// <param name="ptr">A pointer to string data.</param> 436 /// <param name="valLength"> 437 /// Length of the data pointed to by <paramref name="ptr"/>. 438 /// </param> 439 /// <returns> 440 /// An array of strings; one for each null found in the array of characters pointed 441 /// at by <paramref name="ptr"/>. 442 /// </returns> 443 private static string[] ConvertNullSeperatedStringToStringArray(IntPtr ptr, int valLength) 444 { 445 string[] retval; 446 447 if (valLength == 0) 448 { 449 //Return an empty array. 450 retval = new string[0]; 451 } 452 else 453 { 454 //Convert the buffer into a string. Decrease the length 455 //by 1 so that we remove the second null off the end. 456 string buff = Marshal.PtrToStringAuto(ptr, valLength - 1); 457 458 //Parse the buffer into an array of strings by searching for nulls. 459 retval = buff.Split('