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

原文地址:https://www.cnblogs.com/bluejoe/p/5116041.html