项目里用了很多ini配置文件,并且读写非常频繁。
之前一直用的是调用系统的方法
1 private readonly string _inipath; 2 [DllImport("kernel32", CharSet = CharSet.Auto)] 3 private static extern long WritePrivateProfileString(string section, string key, string val, string filePath); 4 5 [DllImport("kernel32", CharSet = CharSet.Auto)] 6 private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath);
但最近要改进项目支持维语,发现ini的读写就有问题了
首先是系统的方法不认识维语的文件名
后来花了大把的力气,把文件名改成英文的,结果发现,不能正常读取和写入
无耐只能放弃使用系统的读取方法,在博客园里找到 @幸福★星 https://www.cnblogs.com/xingfustar/archive/2007/09/13/891950.html
这篇博文,就拿来用了,在这里感谢一下@幸福★星
放上去后简单测试发现,效率很低,因为项目里的配置文件多且每个文件的配置项也很多,频繁读取和保存导致系统很卡
就在这个代码上做了一下优化,见正面的代码:
1 /*---------------------------------------------------------------- 2 * http://XingFuStar.cnblogs.com 3 *---------------------------------------------------------------*/ 4 /*---------------------------------------------------------------- 5 * 6 * 为保证代码的正确性,对INI文件做如下要求: 7 * 1、以";"开头代表注释,注释要另起一行,不要使用非";"开头的注释 8 * 2、如果使用了非";"开头的注释,该行中不要包括“=” 9 * 3、如果使用了非";"开头的注释,对INI进行设置生,该行会被删除 10 * 11 *---------------------------------------------------------------*/ 12 public class IniConfig 13 { 14 private string iniPath = ""; 15 private bool isConfig; 16 private Dictionary<string, List<Property>> propertyList; 17 18 /// <summary> 19 /// 构造函数:装载配置文件 20 /// </summary> 21 /// <param name="iniPath">配置文件的路径</param> 22 public IniConfig(string iniPath) 23 { 24 this.IniPath = iniPath; 25 } 26 27 public string IniPath 28 { 29 set 30 { 31 iniPath = value; 32 isConfig = OnIniPataChanged(); 33 } 34 } 35 36 37 /// <summary> 38 /// 读取Ini中的配置 39 /// </summary> 40 /// <param name="section">节点</param> 41 /// <param name="key">键</param> 42 /// <returns>读取值</returns> 43 public string ReadValue(string section, string key) 44 { 45 string value = ""; 46 bool isRead = false; 47 try 48 { 49 if (propertyList != null && propertyList.ContainsKey(section)) 50 { 51 Property item = propertyList[section].FirstOrDefault(p => p.Key == key); 52 if (item != null) return item.Value; 53 } 54 } 55 catch (Exception ex) 56 { 57 Debug.Print(ex.Message); 58 } 59 return value; 60 } 61 62 /// <summary> 63 /// 向INI中写入配置 64 /// </summary> 65 /// <param name="section">节点</param> 66 /// <param name="key">键</param> 67 /// <param name="value">要写入的新键值</param> 68 /// <returns>写入是否成功</returns> 69 public void WriteValue(string section, string key, string value) 70 { 71 bool isWrite = false; 72 try 73 { 74 if (isConfig) 75 { 76 InitSection(section); 77 Property item = propertyList[section].FirstOrDefault(p => p.Key == key); 78 if (item != null) 79 { 80 item.Value = value; 81 } 82 else 83 { 84 item = new Property(key,value,""); 85 propertyList[section].Add(item); 86 } 87 StartSaveIni(); 88 89 90 } 91 } 92 catch (Exception ex) 93 { 94 Debug.Print(ex.Message); 95 } 96 // return isWrite; 97 } 98 99 private long _savingThreads = 0;//是否在执行写入中 100 private void StartSaveIni() 101 { 102 Action action = () => 103 { 104 if (Interlocked.Read(ref _savingThreads) > 1) return; 105 Interlocked.Increment(ref _savingThreads); 106 System.Threading.Thread.Sleep((int)_savingThreads * 500); 107 SaveIni(); 108 Interlocked.Decrement(ref _savingThreads); 109 }; 110 action.BeginInvoke(null, null); 111 } 112 113 114 115 /// <summary> 116 /// 移除键值对 117 /// </summary> 118 /// <param name="section"></param> 119 /// <param name="key"></param> 120 public void RemoveItem(string section, string key) 121 { 122 WriteValue(section, key, null); 123 } 124 125 /// <summary> 126 /// 验证文件是否存在 127 /// </summary> 128 /// <returns>布尔值</returns> 129 public bool IsExistIniFile() 130 { 131 return File.Exists(iniPath); 132 } 133 134 #region 私有方法 135 private bool OnIniPataChanged() 136 { 137 bool isLoad = false; 138 try 139 { 140 if (File.Exists(iniPath)) 141 { 142 isLoad = LoadIni(); 143 } 144 } 145 catch (Exception ex) 146 { 147 Debug.Print(ex.Message); 148 } 149 return isLoad; 150 } 151 152 /// <summary> 153 /// 从文件中加载INI配置信息到列表 154 /// </summary> 155 /// <returns>加载是否成功</returns> 156 private bool LoadIni() 157 { 158 bool isLoad = false; 159 try 160 { 161 propertyList = new Dictionary<string, List<Property>>(); 162 using (StreamReader stream = new StreamReader(iniPath, System.Text.Encoding.Unicode)) 163 { 164 string section = ""; 165 while (stream.Peek() != -1) 166 { 167 string str = stream.ReadLine().Trim(); 168 //判断该行是否有数据 169 if (str.Length > 0) 170 { 171 //以“;”开头的行为注释行(硬性规定) 172 if (str.Substring(0, 1) != ";") 173 { 174 //以“[“开头的行为Section行(硬性规定) 175 if (str.Substring(0, 1) == "[") 176 { 177 //记录当前Section 178 section = str.Substring(1, str.IndexOf("]") - 1); 179 InitSection(section); 180 } 181 //有“=”的为数据行(硬性规定) 182 if (str.IndexOf("=") > 0 && !String.IsNullOrEmpty(section)) 183 { 184 string[] temp = str.Split('='); 185 //将该数据行的属性添加到列表 186 InitSection(section); 187 propertyList[section].Add(new Property(temp[0].Trim(), temp[1].Trim(), "")); 188 } 189 } 190 else if (!String.IsNullOrEmpty(section)) 191 { 192 //将注释行的属性添加到列表 193 InitSection(section); 194 propertyList[section].Add(new Property("", "", str)); 195 } 196 } 197 else if (!String.IsNullOrEmpty(section)) 198 { 199 InitSection(section); 200 //为保证格式与加载前相同,因些将空行也加入了列表 201 propertyList[section].Add(new Property("", "", "")); 202 } 203 } 204 stream.Close(); 205 } 206 isLoad = true; 207 } 208 catch (Exception ex) 209 { 210 Debug.Print(ex.Message); 211 } 212 return isLoad; 213 } 214 215 private void InitSection(string section) 216 { 217 if(propertyList == null)propertyList = new Dictionary<string, List<Property>>(); 218 if(!propertyList.ContainsKey(section)) 219 propertyList.Add(section,new List<Property>()); 220 } 221 222 /// <summary> 223 /// 将列表中的配置信息保存到INI文件 224 /// </summary> 225 /// <returns>保存是否成功</returns> 226 private bool SaveIni() 227 { 228 bool isSave = false; 229 try 230 { 231 using (StreamWriter stream = new StreamWriter(iniPath, false, System.Text.Encoding.Unicode)) 232 { 233 foreach (var section in propertyList) 234 { 235 stream.WriteLine("[" + section.Key + "]"); 236 237 List<Property> items = section.Value; 238 foreach (var item in items) 239 { 240 if(item.NeedDelete)continue; 241 //写入注释 242 if (item.Description != "") 243 { 244 stream.WriteLine(item.Description); 245 } 246 //写入键和键值 247 if (item.Key != "") 248 { 249 stream.WriteLine(item.Key + " = " + item.Value); 250 } 251 //写入空行 252 if (item.Key == "" && item.Description == "" && item.Value == "") 253 { 254 stream.WriteLine(""); 255 } 256 } 257 } 258 stream.Close(); 259 } 260 261 262 isSave = true; 263 } 264 catch (Exception ex) 265 { 266 Debug.Print(ex.Message); 267 } 268 return isSave; 269 } 270 271 /// <summary> 272 /// 内部类:Ini属性 273 /// </summary> 274 private class Property 275 { 276 /// <summary> 277 /// 构造函数 278 /// </summary> 279 /// <param name="key"></param> 280 /// <param name="value"></param> 281 /// <param name="description"></param> 282 public Property(string key, string value, string description) 283 { 284 this.Key = key; 285 this.Value = value; 286 this.Description = description; 287 } 288 string key = ""; 289 /// <summary> 290 /// 键 291 /// </summary> 292 public string Key 293 { 294 get { return key; } 295 set { key = value; } 296 } 297 string value = ""; 298 /// <summary> 299 /// 键值 300 /// </summary> 301 public string Value 302 { 303 get { return this.value; } 304 set { this.value = value; } 305 } 306 string description = ""; 307 /// <summary> 308 /// 注释 309 /// </summary> 310 public string Description 311 { 312 get { return description; } 313 set { description = value; } 314 } 315 316 public bool NeedDelete { get; set; } 317 318 319 } 320 #endregion 321 }
改进的地方有以下几点:
1.查询由ArrayList+循环改成用Dictionary + List 然后用Linq查询配置项
2.保存加了异步和延时保存
可能存在的问题:
1.因为保加了延时,不知道会不会出现丢失的可能
2.为了支持维语ini文件改成了unicode编码,对于系统默认的ANSI编码支持没有测试过
总结项目中使用ini的配置不能搞的太复杂,太复杂的不能放在ini里做,还是要想别的办法。
你今天为你的梦想努力了吗?