在企业应用中,经常遇到对文本数据的读入和写入问题。如一个移动话单文件,或者使用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对象时,指定的存储类型,将字符串数据还原成相应的类型对象(动态创建),保存在对象列表中。