在企業應用中,經常遇到對文本資料的讀入和寫入問題。如一個移動話單檔案,或者使用SQLServer導出的文本資料,可能會包含這樣的内容:
… 13411112222,XXXX,20060225121800,1000 13512345678,YYYY,20060224101011,800 … |
在解析這些檔案時,要将每行資料拆分成資料單元,然後儲存在預先定義好的資料對象中,一個資料檔案,經過解析,就會形成一個資料對象清單。
對于這類資料檔案,差別隻在于資料的列數、每列資料的含義,以及資料的分隔符,檔案讀入或寫入都是和檔案格式無關的。基于這樣的特性,可以考慮實作一個通用的資料檔案處理子產品,适用于所有這種模式存儲的資料檔案。
要實作上述功能,我們需要把那些不确定的因素(資料的列數、每列資料的含義,以及資料的分隔符)盡量推遲到使用者實用階段,也就是把單獨一行的資料交給使用者定義的對象來解析,同時也允許使用者将生成好的一行資料交給我們的子產品來存儲。另外為了使序列化後的資料通路更快,這個記錄表在記憶體中應該使用哈希表來存儲,支援主鍵索引。
先定義兩個接口類,限定使用者定義的對象的特性:支援一行資料的解析和生成、支援主鍵索引。
/// <summary> /// 接口,表示可以從對象中得到主鍵 /// guanzhong 2005 /// http://blog.csdn.net/guanzhongs /// </summary> public interface IHashable { /// <summary> /// 獲得對象中的主鍵字段 /// </summary> /// <returns></returns> Object GetKey(); } /// <summary> /// 接口,表示可以将對象以文本方式導入導出到資料檔案中 /// guanzhong 2005 /// http://blog.csdn.net/guanzhongs /// </summary> public interface ITextSerialisable : IHashable { /// <summary> /// 可以将輸入字元串解釋為對象的屬性 /// </summary> /// <param name="str"></param> /// <returns>傳回載入的字段數</returns> Int32 SerialIn(string str); /// <summary> /// 與 SerialIn 接口逆向,将對象屬性生成為字元串 /// </summary> /// <returns></returns> String SerialOut(); } |
當使用者希望他的資料對象支援文本記錄的序列化時,隻需要從ITextSerialisable派生即可。
下面代碼實作了具體的檔案資料讀取:
/// <summary> /// 散清單資料的檔案存儲類 /// 存儲時隻能接受從 ITextSerialisable 派生的對象 /// 在 new 一個新對象時,需要指定所存儲資料類型 /// guanzhong 2005 /// http://blog.csdn.net/guanzhongs /// 效率測試 /// 插入記錄 1000毫秒/10萬條 /// 寫入檔案 250毫秒/10萬條 /// 載入檔案 1800毫秒/10萬條 /// </summary> public class FileData { private HashedList listData = null; System.Type typeData; /// <summary> /// 構造函數 /// </summary> /// <param name="type">指定所存儲的資料類型,改類型必須從 ITextSerialisable 派生</param> public FileData(System.Type type) { listData = new HashedList(); typeData = type; } /// <summary> /// 添加記錄 /// </summary> /// <param name="itsa"></param> /// <returns></returns> public bool Add(ITextSerialisable itsa) { if(itsa.GetType() != typeData) { Exception ex = new Exception(string.Format("Invalid type of input parameter, only type /"{0}/" is accceptable",typeData.ToString())); throw(ex); } return listData.Add(itsa); } /// <summary> /// 根據主鍵擷取對象 /// </summary> /// <param name="key"></param> /// <returns></returns> public Object Get(Object key) { return listData.Get(key); } /// <summary> /// 删除指定對象 /// </summary> /// <param name="itsa"></param> /// <returns></returns> public bool Delete(ITextSerialisable itsa) { if(itsa.GetType() != typeData) { Exception ex = new Exception(string.Format("Invalid type of input parameter, only type /"{0}/" is accceptable",typeData.ToString())); throw(ex); } return listData.Delete(itsa); } /// <summary> /// 根據主鍵删除記錄 /// </summary> /// <param name="key"></param> /// <returns></returns> public bool Delete(Object key) { return listData.Delete(key); } /// <summary> /// 清除儲存的所有對象 /// </summary> public void Clear() { listData.Clear(); } /// <summary> /// 從檔案加載記錄 /// </summary> /// <param name="strFileName"></param> /// <returns>讀入記錄的行數</returns> public int Load(string strFileName) { if(strFileName == null || strFileName == "") { return -1; } FileStream stream = File.OpenRead(strFileName); if(stream == null) { return -1; } byte[] buf = new byte[stream.Length]; int ptr = 0; int nlen = buf.Length; // 讀取檔案資料 while(nlen > 0) { int nReaded = stream.Read(buf,ptr,nlen); if(nReaded == 0) { break; } ptr += nReaded; nlen -= nReaded; } stream.Close(); string strContent = Encoding.Default.GetString(buf); string[] lines = strContent.Split('/n'); // 處理每一行 int nRead = 0; foreach(string strLine in lines) { string strTmpLine = strLine.TrimEnd('/r'); // 如果檔案是以"/r/n"換行,則使用'/n'分割後每行末尾會遺留一個'/r'符,是以将其去掉 // 讀取一行的記錄 ITextSerialisable itsa = (ITextSerialisable)System.Activator.CreateInstance(typeData); if(itsa.SerialIn(strTmpLine) > 0) { if(listData.Add(itsa)) { nRead++; } } } return nRead; } /// <summary> /// 将記錄儲存到檔案 /// </summary> /// <param name="strFileName"></param> /// <returns>寫入的記錄數</returns> public int Store(string strFileName) { if(strFileName == null || strFileName == "") { return -1; } StreamWriter writer = File.CreateText(strFileName); if(writer == null) { return -1; } int nWrite = 0; for(int i=0;i<listData.Count;i++) { ITextSerialisable itsa = (ITextSerialisable)listData.GetAt(i); writer.WriteLine(itsa.SerialOut()); nWrite++; } writer.Close(); return nWrite; } public HashedList Datas { get { return listData; } } public int Count { get { return listData.Count; } } /// <summary> /// 散列記錄集 /// guanzhong 2005 /// http://blog.csdn.net/guanzhongs /// </summary> public class HashedList : ICollection { private SortedList list = null; private ArrayList array = null; // 用于有序的存放清單元素,使得可以在GetEnumerator可以傳回IEnumerator對象 public HashedList() { list = new SortedList(); array = new ArrayList(); } /// <summary> /// 以對象指定的主鍵将對象插入到清單中 /// </summary> /// <param name="iek"></param> /// <returns></returns> public bool Add(IHashable iha) { if(iha == null) { return false; } if(iha.GetKey() == null) { Exception ex = new Exception(string.Format("Null hash key of object /"{0}/"",typeof(IHashable).ToString())); throw(ex); } list.Remove(iha.GetKey()); // 删除舊的記錄(如果有的話) list.Add(iha.GetKey(),iha); // 加入新的記錄 return true; } /// <summary> /// 根據主鍵獲得對象 /// </summary> /// <param name="key"></param> /// <returns></returns> public Object Get(Object key) { if(key == null) { return null; } return list[key]; } /// <summary> /// 根據索引獲得對象 /// </summary> /// <param name="index"></param> /// <returns></returns> public Object GetAt(int index) { return list.GetByIndex(index); } /// <summary> /// 删除指定對象,必須保證對象中的主鍵有效 /// </summary> /// <param name="iek"></param> /// <returns></returns> public bool Delete(IHashable iha) { if(iha == null) { return false; } if(iha.GetKey() == null) { Exception ex = new Exception(string.Format("Null hash key of object /"{0}/"",typeof(IHashable).ToString())); throw(ex); } list.Remove(iha.GetKey()); return true; } /// <summary> /// 根據主鍵删除記錄 /// </summary> /// <param name="key"></param> /// <returns></returns> public bool Delete(Object key) { list.Remove(key); return true; } /// <summary> /// 清除清單 /// </summary> public void Clear() { list.Clear(); } /// <summary> /// 将對象複制到一維清單中,以便可以隻用foreach進行周遊 /// </summary> /// <param name="array"></param> private void CopyToArrayList(ref ArrayList array) { array.Clear(); for(int i=0;i<Count;i++) { Object obj = list.GetByIndex(i); array.Add(obj); } } #region ICollection Members public bool IsSynchronized { get { // TODO: Add PaydataCollection.IsSynchronized getter implementation return false; } } public int Count { get { // TODO: Add PaydataCollection.Count getter implementation return list.Count; } } public void CopyTo(Array array, int index) { // TODO: Add PaydataCollection.CopyTo implementation list.CopyTo(array,index); } public object SyncRoot { get { // TODO: Add PaydataCollection.SyncRoot getter implementation return null; } } #endregion #region IEnumerable Members public IEnumerator GetEnumerator() { // TODO: Add PaydataCollection.GetEnumerator implementation CopyToArrayList(ref array); //return list.GetEnumerator(); return array.GetEnumerator(); } #endregion } // HashedList } |
在Load方法中,這幾行
// 讀取一行的記錄 ITextSerialisable itsa = (ITextSerialisable)System.Activator.CreateInstance(typeData); if(itsa.SerialIn(strTmpLine) > 0) { if(listData.Add(itsa)) { nRead++; } } |
是關鍵,它根據使用者在初始化FileData對象時,指定的存儲類型,将字元串資料還原成相應的類型對象(動态建立),儲存在對象清單中。