zoukankan      html  css  js  c++  java
  • 6.3.3 变长数据块的读写

    6.3.3  变长数据块的读写

    变长数据块的含义为每一段数据的长度是不固定的。设计这种数据的关键是在某个已知的位置记录每段记录的长度,这有点像Windows API中的某些结构的定义,它们的第一个字节会标识本结构的长度。比如创建进程时,CreateProcess()函数需要一个STARTUPINFO结构的参数,STARTUPINFO结构的定义如下:

    1. typedef struct _STARTUPINFO {  
    2.   DWORD cb;  
    3.   LPTSTR lpReserved;  
    4.   LPTSTR lpDesktop;  
    5.   LPTSTR lpTitle;  
    6.   DWORD dwX;  
    7.   DWORD dwY;  
    8.   DWORD dwXSize;  
    9.   DWORD dwYSize;  
    10.   DWORD dwXCountChars;  
    11.   DWORD dwYCountChars;  
    12.   DWORD dwFillAttribute;  
    13.   DWORD dwFlags;  
    14.   WORD wShowWindow;  
    15.   WORD cbReserved2;  
    16.   LPBYTE lpReserved2;  
    17.   HANDLE hStdInput;         //标准输入  
    18.   HANDLE hStdOutput;        //标准输出  
    19.   HANDLE hStdError;         //标准错误  
    20. } STARTUPINFO, *LPSTARTUPINFO; 

    其第一个成员cb就负责指明当前结构块的字节长度,一般我们需要将其设置为:

    1. STARTUPINFO sui;  
    2. sui.cb = sizeof(STARTUPINFO); 

    提示

    针对以上cb成员的赋值,很多程序员可能会觉得有点多此一举,sizeof(STARTUPINFO)是静态不变的,那为什么还要浪费一个成员来存储自己的长度呢?唯一合理的解释是:Windows可能是处于版本升级的考虑,在不同的Windows版本下,STARTUPINFO可能会具有不同的长度。

    在写文件时,如果待写数据段的长度不固定,则将其长度记录在该段相对固定的位置上,当文件读取程序遇到该段数据时,读取到长度值。

    现在动手

    如下程序为变长数据块的读写,我们将数据块的长度记录在每段数据块的头部,请读者仔细体验。

    【程序6-5】使用CFile完成变长数据块的读写

    1. 01  #include "stdafx.h" 
    2. 02    
    3. 03  //写入字符串  
    4. 04  void WriteString(CFile & file, CString & s)  
    5. 05  {  
    6. 06      int len = s.GetLength();  
    7. 07      //写入字符串的长度  
    8. 08      file.Write(&len, sizeof(int));  
    9. 09      //写入字符串  
    10. 10      file.Write((LPCTSTR)s, len);  
    11. 11  }  
    12. 12    
    13. 13  //读取字符串  
    14. 14  bool ReadString(CFile & file, CString & s)  
    15. 15  {  
    16. 16      //先读取字符串长度  
    17. 17      int len;  
    18. 18      if(file.Read(&len, sizeof(int)) == sizeof(int))  
    19. 19      {  
    20. 20          TRACE("字符串长度: %d bytes/r/n", len);  
    21. 21        
    22. 22          char * sp = new char[len + 1];  
    23. 23          sp[len] = 0;  
    24. 24          if(file.Read(sp, len) == len)  
    25. 25          {  
    26. 26              s = sp;  
    27. 27              delete [] sp;  
    28. 28              return true;  
    29. 29          }  
    30. 30          delete [] sp;  
    31. 31      }  
    32. 32    
    33. 33      return false;  
    34. 34  }  
    35. 35    
    36. 36  //读取指定索引的字符串  
    37. 37  bool ReadString(CFile & file, CString & s, int index)  
    38. 38  {  
    39. 39      //比较麻烦,必须从头开始数  
    40. 40      file.SeekToBegin();  
    41. 41      int i = 0;  
    42. 42      while(i < index)  
    43. 43      {  
    44. 44          //读取记录长度  
    45. 45          int len;  
    46. 46          if(file.Read(&len, sizeof(int)) != sizeof(int))  
    47. 47              return false;  
    48. 48    
    49. 49          //定位文件指针  
    50. 50          file.Seek(len, CFile::current);  
    51. 51          i++;  
    52. 52      }  
    53. 53    
    54. 54      return ReadString(file, s);  
    55. 55  }  
    56. 56    
    57. 57  int main()  
    58. 58  {  
    59. 59      CFile file;  
    60. 60      file.Open("test.out", CFile::modeWrite
      | CFile::modeCreate);  
    61. 61        
    62. 62      CString s1 = "bluejoe";  
    63. 63      CString s2 = "jerry";  
    64. 64      CString s3 = "even";  
    65. 65    
    66. 66      //写入  
    67. 67      WriteString(file, s1);  
    68. 68      WriteString(file, s2);  
    69. 69      WriteString(file, s3);  
    70. 70    
    71. 71      file.Close();  
    72. 72      //读取  
    73. 73      file.Open("test.out", CFile::modeRead);  
    74. 74      printf("文件大小: %d bytes/r/n", file.GetLength());  
    75. 75    
    76. 76      int i = 0;  
    77. 77      while(true)  
    78. 78      {  
    79. 79          CString s;  
    80. 80          if(!ReadString(file, s))  
    81. 81              break;  
    82. 82    
    83. 83          printf("[%d]: %s/r/n", i, s);  
    84. 84          i++;  
    85. 85      }  
    86. 86    
    87. 87      CString s;  
    88. 88      if(ReadString(file, s, 1))  
    89. 89          printf("[%d]: %s/r/n", 1, s);  
    90. 90    
    91. 91      return 0;92 } 

    运行结果如图6-16所示。

     
    图6-16  运行结果
    调试运行时,TRACE()会产生如下输出:
     

    1. 字符串长度: 7 bytes  
    2. 字符串长度: 5 bytes  
    3. 字符串长度: 4 bytes  
    4. 字符串长度: 5 bytes 

    可以看出,采用变长数据块来存储字符串,可以大大节省存储空间。其原理如图6-17所示。很多应用程序都会采用类似的存储方式,但是它们往往更喜欢将每一段数据块的描述信息(如:偏移量、长度)统一地记录在文件的首部。
     

      
    图6-17  变长数据块的读写

    我们可以通过记事本等工具来观察两种形式的数据文件的内容,如图6-18所示。
     

     
    图6-18  定长数据与变长数据

    光盘导读

    该项目对应于光盘中的目录"/ch06/VarSizedBlock"。
     

    以上摘自《把脉VC++》第6.3.3小节的内容 ,转载请注明出处。

    如果你想与我交流,请点击如下链接加我为好友:http://student.csdn.net/invite.php?u=113292&c=8913f87cffe7d533

  • 相关阅读:
    ASP.NET2.0中GridView加入CheckBox实现全选!
    恢复误删数据(SQL Server 2000)--Log Explorer
    url传递中文的解决方案总结
    JavaScript : Tip提示框。
    合并GridView中某列相同信息的行
    ASP.NET 2.0服务器控件与form runat=server标记 !!
    实现天气预报类···························
    正则抓取SINA天气预报数据!!!
    ASP.NET 2.0中将 GridView 导出到 Excel 文件中
    GridView控件修改、删除示例(修改含有DropDownList控件)
  • 原文地址:https://www.cnblogs.com/bluejoe/p/5116041.html
Copyright © 2011-2022 走看看