对txt配置文件的 读取架构 1
索引,文本
名字_龙飞阵,龙飞阵
名字_天覆阵,天覆阵
名字_地载阵,地载阵
名字_云垂阵,云垂阵
名字_风扬阵,风扬阵
名字_暴怒阵,暴怒阵
对于一堆这样的配置文件 ,如何对它进行解析呢?
程序的结构和表结构息息相关,这里的各个表间的衔接通过一个表索引链接起来,程序的设计也是从这里开始;
DataTable所有表结构的基类,封装了表 数据 的加载, 解析。上代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 using UnityEngine; 2 using System; 3 using System.Collections; 4 using System.Collections.Generic; 5 using System.IO; 6 using System.Text.RegularExpressions; 7 using System.Text; 8 9 /// <summary> 10 /// 配置表解析抽象类 11 /// </summary> 12 public abstract class DataTable 13 { 14 protected int currLineValueCount; 15 protected string[] currLineValues; 16 private const string FILE_ENCODING = "GBK"; 17 /// <summary>当前加载的文件路径的数组下标索引 </summary> 18 private int fileIndex=0; 19 private string[] filePaths; 20 /// <summary> 以每行具体的值为键,键有空.该值的列数为值,从零开始</summary> 21 private Dictionary<string , int> name2Index; 22 23 /// <summary> 24 /// 25 /// </summary> 26 /// <param name="paths">加载文件的路径</param> 27 public DataTable(string[] paths) 28 { 29 this.filePaths = paths; 30 } 31 public DataTable(string path) 32 { 33 this.filePaths = new string[] { path }; 34 } 35 36 #region 数值获取 37 /// <summary> 38 /// 删除包含整体文本的“ ” 39 /// </summary> 40 /// <param name="s">要去除“ ” 的包含的整体文本值</param> 41 /// <returns></returns> 42 private string deleteQuotation(string s) 43 { 44 //string的 索引的是char ,是特性实现的。 string的 前后 是 “” 45 if(((s.Length>0)&&(s[0]=='"'))&&(s[s.Length-1]=='"')) 46 { 47 //去点第一个 和最后一个 48 s = s.Substring(1 , s.Length - 2); 49 } 50 return s; 51 } 52 53 /// <summary> 54 /// 返回的是当前行里的 Name的值 ,如果存在的话 55 /// </summary> 56 /// <param name="name"></param> 57 /// <returns></returns> 58 protected string GetValueByName(string name) 59 { 60 int num=0; 61 //字典中是否已存在 索引不超过本行所在的数量 本字符串要大于零 62 if((this.name2Index.TryGetValue(name,out num)&&(num<this.currLineValueCount))&&(this.currLineValues[num].Length>0)) 63 { 64 string str=this.currLineValues[num]; 65 //存的 null ... 66 if(str!="null") 67 { 68 return str; 69 } 70 } 71 return null; 72 } 73 74 /// <summary> 75 /// 看当前 字典里是否存在 Name的值(是否是真是数据) 76 /// </summary> 77 /// <param name="name"></param>要查找的值 78 /// <param name="defaultValue"></param> 79 /// <param name="mustExist"></param> 80 /// <returns></returns> 81 protected bool GetBoolean(string name,bool defaultValue,bool mustExist) 82 { 83 string valueByName = this.GetValueByName(name); 84 if(valueByName==null) 85 { 86 if(mustExist) 87 { 88 throw new ColumnNotFoundException(name); 89 } 90 return defaultValue; 91 } 92 int result = 1; 93 if(!int.TryParse(valueByName,out result)) 94 { 95 throw new ParseException(valueByName); 96 } 97 return (result == 1); 98 } 99 100 /// <summary> 101 /// 获取 一张 配置表里的 值 如果有的话 102 /// </summary> 103 /// <param name="name">要获取的string 名</param> 104 /// <param name="defaultValue">默认值</param> 105 /// <param name="mustExist"> 是否值 必须存在</param> 106 /// <returns></returns> 107 protected string GetString(string name , string defaultValue , bool mustExist) 108 { 109 string valueByName = this.GetValueByName(name); 110 if (valueByName != null) 111 { 112 return valueByName; 113 } 114 if (mustExist) 115 { 116 throw new ColumnNotFoundException(name); 117 } 118 return defaultValue; 119 } 120 121 protected long GetLong(string name , long defaultValue , bool mustExist) 122 { 123 string valueByName = this.GetValueByName(name); 124 if (valueByName == null) 125 { 126 if (mustExist) 127 { 128 throw new ColumnNotFoundException(name); 129 } 130 return defaultValue; 131 } 132 long result = 0L; 133 if(!long.TryParse(valueByName,out result)) 134 { 135 throw new ParseException(valueByName); 136 } 137 return result; 138 } 139 protected double GetDouble(string name,double defaultValue ,bool mustExist) 140 { 141 string valueByName = this.GetValueByName(name); 142 if(valueByName==null) 143 { 144 if(mustExist) 145 { 146 throw new ColumnNotFoundException(name); 147 } 148 return defaultValue; 149 } 150 double result = 0.0; 151 if(!double.TryParse(valueByName,out result)) 152 { 153 throw new ParseException(valueByName); 154 } 155 return result; 156 } 157 158 protected int GetInt(string name , int defaultValue , bool mustExist) 159 { 160 string valueByName = this.GetValueByName(name); 161 if(valueByName==null) 162 { 163 if(mustExist) 164 { 165 throw new ColumnNotFoundException(name); 166 } 167 return defaultValue; 168 } 169 int result = 0; 170 if(!int.TryParse(valueByName,out result)) 171 { 172 throw new ParseException(valueByName); 173 } 174 return result; 175 } 176 177 protected float GetFloat(string name , float defaultValue , bool mustExist) 178 { 179 string valueByName = this.GetValueByName(name); 180 if (valueByName == null) 181 { 182 if (mustExist) 183 { 184 throw new ColumnNotFoundException(name); 185 } 186 return defaultValue; 187 } 188 float result = 0f; 189 if (!float.TryParse(valueByName , out result)) 190 { 191 throw new ParseException(valueByName); 192 } 193 return result; 194 } 195 196 /// <summary> 197 /// 看用在哪里 ,来判断他的用处 198 /// </summary> 199 /// <param name="name"></param> 200 /// <param name="mustExist"></param> 201 /// <returns></returns> 202 protected DynamicParam GetDyncmicParam(string name, bool mustExist) 203 { 204 string s = this.GetString(name , string.Empty , mustExist); 205 int result = 0; 206 int.TryParse(s , out result); 207 double num2 = 0.0; 208 double.TryParse(s , out num2); 209 return new DynamicParam(s , result , num2); 210 } 211 212 /// <summary> 213 /// 获取一个数据集 214 /// </summary> 215 /// <param name="name"></param> 216 /// <param name="mustExist"></param> 217 /// <returns></returns> 218 protected DataSet GetDataSet(string name,bool mustExist) 219 { 220 return new DataSet(name ,this.GetString(name,! mustExist ? string.Empty : null , mustExist)); 221 } 222 #endregion 223 224 #region 数据 类 225 226 /// <summary> 227 /// 228 /// </summary> 229 public class DynamicParam 230 { 231 public double f; 232 public int n; 233 public string str; 234 235 public DynamicParam(string str_,int n_ ,double f_) 236 { 237 this.f = f_; 238 this.str = str_; 239 this.n = n_; 240 } 241 } 242 243 /// <summary>栏数据的特殊集 </summary> 244 public class DataSet 245 { 246 private string columnName; 247 /// <summary>光标</summary> 248 private int cursor; 249 250 /// <summary> 251 /// 取得的每一行的文本 不是通过StreamRead 的方式 252 /// </summary> 253 private MatchCollection result; 254 255 /// <summary>当前行的 信息</summary> 256 private string[] values; 257 public DataSet(string colName,string s) 258 { 259 if(s=="") 260 { 261 s = "\"(宝箱_金宝箱,0)\""; 262 } 263 //(?<=\() 匹配(后面的字符, (.*?)匹配 任意多个字符 ,(?=\))匹配 )前面的字符 264 this.result = Regex.Matches(s , @"(?<=\()(.*?)(?=\))"); 265 this.columnName = colName; 266 } 267 268 #region 数据获取函数 269 /// <summary> 270 /// 通过索引获取double类型的值 271 /// </summary> 272 /// <param name="index"></param> 273 /// <returns></returns> 274 public double GetDouble(int index) 275 { 276 if(index >= this.values.Length) 277 { 278 throw new DataTable.ColumnNotFoundException(this.columnName); 279 } 280 double result = 0.0; 281 //不是能转换的格式 282 if(!double.TryParse(this.values[index],out result)) 283 { 284 throw new DataTable.ParseException(this.values[index]); 285 } 286 return result; 287 } 288 289 public float GetFloat(int index) 290 { 291 if(index>=this.values.Length) 292 { 293 throw new DataTable.ColumnNotFoundException(this.columnName); 294 } 295 float result = 0f; 296 if(!float.TryParse(this.values[index],out result)) 297 { 298 throw new DataTable.ParseException(this.values[index]); 299 } 300 return result; 301 } 302 303 public int GetInt(int index) 304 { 305 if (index >= this.values.Length) 306 { 307 throw new DataTable.ColumnNotFoundException(this.columnName); 308 } 309 int result =0; 310 if (!int.TryParse(this.values[index] , out result)) 311 { 312 throw new DataTable.ParseException(this.values[index]); 313 } 314 return result; 315 } 316 317 public long GetLong(int index) 318 { 319 if (index >= this.values.Length) 320 { 321 throw new DataTable.ColumnNotFoundException(this.columnName); 322 } 323 long result = 0L; 324 if (!long.TryParse(this.values[index] , out result)) 325 { 326 throw new DataTable.ParseException(this.values[index]); 327 } 328 return result; 329 } 330 331 public string GetString(int index) 332 { 333 if (index >= this.values.Length) 334 { 335 throw new DataTable.ColumnNotFoundException(this.columnName); 336 } 337 return this.values[index]; 338 } 339 #endregion 340 341 /// <summary> 342 /// 开放当前行的数据集 343 /// </summary> 344 /// <returns></returns> 345 public string[] Values() 346 { 347 return this.values; 348 } 349 /// <summary> 350 /// 下个值是否存在 351 /// </summary> 352 public bool NextValues() 353 { 354 //超出了行数 结束 355 if(this.cursor>=this.result.Count) 356 { 357 return false; 358 } 359 //获取当前 行(cursor 光标所在) 的行文本 360 string str = this.result[this.cursor].Value; 361 char[] sepatator = new char[] { ',' }; 362 this.values = str.Split(sepatator); 363 // 同时对当前行的文本进行处理,这样在获取瞎哥值是否存在时,也能获取当前行的值 364 for (int i=0;i<this.values.Length;i++) 365 { 366 this.values[i] = this.values[i].Trim(); 367 } 368 this.cursor++; 369 return true; 370 } 371 372 } 373 #endregion 374 375 #region 文件加载 376 377 /// <summary> 378 /// isPack 是打包通过unity加载 ,否者通过 C# 加载 379 /// </summary> 380 /// <returns></returns> 381 public bool Load() 382 { 383 if (this.filePaths == null) 384 { 385 return false; 386 } 387 this.currLineValues = new string[0x400]; 388 this.currLineValueCount = 0; 389 for (int i = 0 ; i < this.filePaths.Length ; i++) 390 { 391 string path = this.filePaths[i]; 392 path = DataMngr._GetDirPath() + path+".txt"; 393 //+DataMngr._GetExtName(); 394 bool parseColumn = true; 395 if (!(!DataMngr._isPack() ? this.LoadByFile(path , parseColumn) : this.LoadByUnity(path , parseColumn))) 396 { 397 return false; 398 } 399 } 400 this.currLineValues = null; 401 this.currLineValueCount = 0; 402 this.OnLoadComplete(); 403 return true; 404 } 405 406 407 /// <summary> 408 /// 加载txt配置文件 409 /// </summary> 410 /// <param name="path">文件路径</param> 411 /// <param name="parseColumn">是否加载栏</param> 412 /// <returns>是否加载成功</returns> 413 private bool LoadByFile(string path,bool parseColumn) 414 { 415 FileStream stream = null; 416 try 417 { 418 stream = new FileStream(path , FileMode.Open , FileAccess.Read , FileShare.ReadWrite); 419 } 420 catch 421 { 422 return false; 423 } 424 StreamReader reader = null; 425 try 426 { 427 reader = new StreamReader(stream ,Encoding.UTF8); 428 } 429 catch 430 {//关闭流 ,取消引用 431 stream.Close(); 432 stream = null; 433 return false; 434 } 435 436 int lineNum = 0; 437 bool flag = true; 438 while(!reader.EndOfStream) 439 { 440 string lineStr = reader.ReadLine(); 441 //栏 442 if(lineNum==0) 443 { 444 if(parseColumn) 445 {//第一栏 446 this.ParseColumn(lineStr); 447 } 448 } 449 else if(!this.ParseData(lineStr,lineNum)) 450 {//转换出错 451 flag = false; 452 Debug.LogError(string.Concat(new string[]{"file parse error.path=",path,",line=",lineNum.ToString()})); 453 break; 454 } 455 lineNum++; 456 } 457 reader.Close(); 458 reader = null; 459 stream.Close(); 460 stream = null; 461 return flag; 462 } 463 464 private bool LoadByUnity(string path,bool parseColumn) 465 { 466 int num3=0; 467 //所有资源打包为AssetsBundle 然后通过路径加载 468 Res res = ResMngr.GetInstance().LoadSync("Data" , path); 469 if(res==null) 470 { 471 return false; 472 } 473 TextAsset assetToUnload = (TextAsset)res.GetObject(); 474 if(assetToUnload==null) 475 { 476 return false; 477 } 478 string text = assetToUnload.text; 479 if(text.Length<=0) 480 { 481 return false; 482 } 483 //Windows中每行结尾是“<换行><回车>”,即“\n\r” 484 string str2 = "\r\n"; 485 int lineNum = 0; 486 for(int i=0;i<text.Length;i=num3+str2.Length) 487 { 488 //OrdinalIgnoreCase使用序号排序规则并忽略被比较字符串的大小写,对字符串进行比较 489 num3 = text.IndexOf(str2 , i , StringComparison.OrdinalIgnoreCase); 490 string lineStr = text.Substring(i , num3 - i); 491 if(lineNum==0) 492 { 493 if(parseColumn) 494 { 495 this.ParseColumn(lineStr); 496 } 497 } 498 else if(!this.ParseData(lineStr,lineNum)) 499 { 500 return false; 501 } 502 lineNum++; 503 } 504 Resources.UnloadAsset(assetToUnload); 505 return true; 506 507 } 508 509 //public bool LoadNotFromDataMngr() 510 //{ 511 // if(this.filePaths==null) 512 // { 513 // return false; 514 // } 515 // 0x400 == 1024 516 // this.currLineValues = new string[0x400]; 517 // this.currLineValueCount = 0; 518 // string str = string.Empty; 519 // string serverDataPath = string.Empty; 520 // bool flag = false; 521 // if(RunParam.Getins) 522 523 524 //} 525 526 /// <summary> 527 ///转换第一行的栏 528 /// </summary> 529 /// <param name="lineStr">当前行的文本,这里是第一行栏的文本</param> 530 protected void ParseColumn(string lineStr) 531 { 532 this.name2Index = new Dictionary<string , int>(); 533 //文本的分割器 534 char[] separator = new char[] { ',' }; 535 string[] strArray = lineStr.Split(separator); 536 for(int i=0;i<strArray.Length;i++) 537 { 538 //每行的每个值作为 键 539 string key = strArray[i]; 540 //作为栏 是一定不会键位空值的 541 if(this.name2Index.ContainsKey(key)) 542 { 543 Debug.LogError("File["+this.filePaths[this.fileIndex]+"]colum["+key+"]had exist!"); 544 return; 545 } 546 this.name2Index.Add(key , i); 547 } 548 } 549 550 /// <summary> 551 /// 转换每个配置表里的值 552 /// </summary> 553 /// <param name="lineStr">每行的文本</param> 554 /// <param name="lineNum">文本的所在行位置,第几行</param> 555 /// <returns>GetBoolean 函数定义了抛出异常的条件,这里扑捉异常</returns> 556 protected bool ParseData(string lineStr,int lineNum) 557 { 558 try 559 { 560 //行开头以"//"始 是一个完整的文本 561 if(lineStr.Substring(0,2).Equals("//")) 562 { 563 return true; 564 } 565 } 566 catch(Exception) 567 { 568 //这行数据为空 会报错哦 569 Debug.LogError("ParseData Exception"); 570 } 571 //这里进行 数据的转换 ,包括格式定义的检查 分割,并给当前行赋值 572 this.currLineValueCount = this.SplitColumn(lineStr); 573 try 574 { 575 if(!this.OnParseLine(lineNum)) 576 { 577 Debug.LogError(string.Concat(new string[] { "File[" , this.GetFilePath() , "]Line[" , lineNum.ToString() , "]prototype may had exist!" })); 578 return false; 579 } 580 } 581 catch(ColumnNotFoundException ex) 582 { 583 Debug.LogError(string.Concat(new string[] { "Line[" , lineNum.ToString() , "]could not find\"" , ex.GetColumnName() , "\"column!" })); 584 return false; 585 } 586 catch(ParseException ex1) 587 { 588 Debug.LogError(string.Concat(new string[] { "Line[" , lineNum.ToString() , "]parse failed ! data=\"" , ex1.GetParseData() , "\"" })); 589 return false; 590 } 591 return true; 592 } 593 594 public string GetFilePath() 595 { 596 return this.filePaths[this.fileIndex]; 597 } 598 599 /// <summary> 600 /// 获取栏数,并对栏进行处理,对于有‘”’标记整体的栏值 去‘“’ 处理,currentValues 也一并处理了 601 /// </summary> 602 /// <param name="lineStr">当前的一行文本</param> 603 /// <returns></returns> 604 private int SplitColumn(string lineStr) 605 { 606 int num = 0; 607 bool flag = false; 608 int startIndex = 0; 609 for(int i=0;i<lineStr.Length;i++) 610 { 611 char ch = lineStr[i]; 612 //文本中字符包含 “ 见文本char_rate_params.txt 这是被标记的文本,其中包含‘,’但是是一个整体要特殊处理 613 if(ch=='"') 614 { 615 flag = !flag; 616 } 617 //有 段落值标记时,该段内只一个整体 不对‘,’进行分割 618 else if((ch==',')&&!flag) 619 { 620 string s = lineStr.Substring(startIndex , i - startIndex); 621 startIndex = i + 1; 622 this.currLineValues[num++] = this.deleteQuotation(s); 623 } 624 } 625 //不是用Split 分割 ,最后一个‘,’之后 不会再分割,还要再加上去 626 if(startIndex<lineStr.Length) 627 { 628 string str2 = lineStr.Substring(startIndex , lineStr.Length - startIndex); 629 this.currLineValues[num++] = this.deleteQuotation(str2); 630 } 631 return num; 632 } 633 634 635 636 /// <summary> 637 /// 转换行 638 /// </summary> 639 /// <param name="lineNum">文本所在行数</param> 640 /// <returns></returns> 641 protected abstract bool OnParseLine(int lineNum); 642 /// <summary> 643 /// 构建表数据 644 /// </summary> 645 /// <returns></returns> 646 public abstract bool OnBuild(); 647 648 protected abstract bool OnLoadComplete(); 649 #endregion 650 651 #region 异常类 652 public class ParseException:Exception 653 { 654 private string data; 655 public ParseException(string d) 656 { 657 this.data = d; 658 } 659 public string GetParseData() 660 { 661 return this.data; 662 } 663 } 664 public class ColumnNotFoundException:Exception 665 { 666 private string columnName; 667 public ColumnNotFoundException(string name) 668 { 669 this.columnName = name; 670 } 671 public string GetColumnName() 672 { 673 return this.columnName; 674 } 675 } 676 #endregion 677 678 }