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

  • 相关阅读:
    ECharts之柱状图 饼状图 折线图
    Vue自定义指令(directive)
    HDU 1231 最大连续子序列
    POJ 2533 Longest Ordered Subsequence
    HDU 1163 Eddy's digital Roots
    HDU 2317 Nasty Hacks
    HDU 2571 命运
    HDU 4224 Enumeration?
    HDU 1257 最少拦截系统
    HDU 2740 Root of the Problem
  • 原文地址:https://www.cnblogs.com/bluejoe/p/5116041.html
Copyright © 2011-2022 走看看