CArchive原理

CArchive原理   自撰   2001年9月12日23:58    
   
   
  MFC   提供CArchive类实现数据的缓冲区读写,同时定义了类对象的存储与读取方案。    
   
  以下对CArchvie   的内部实现作分析。    
   
   
   
  1.概述    
   
  2.内部数据    
   
  3.基本数据读写    
   
  4.缓冲区的更新    
   
  5.指定长度数据段落的读写    
   
  6.字符串的读写    
   
  7.CObject派生对象的读写    
   
   
  1.概述    
   
  CArchive使用了缓冲区,即一段内存空间作为临时数据存储地,对CArchive的读写都先依次排列到此缓冲区,当缓冲区满或用户要求时,将此段整理后的数据读写到指定的存储煤质。    
   
  当建立CArchive对象时,应指定其模式是用于缓冲区读,还是用于缓冲区写。    
   
  可以这样理解,CArchive对象相当于铁路的货运练调度站,零散的货物被收集,当总量到达火车运量的时候,由火车装运走。    
   
  当接到火车的货物时,则货物由被分散到各自的货主。与货运不同的是,交货、取货是按时间循序执行的,而不是凭票据。因此必须保证送货的和取货的货主按同样的循序去存或取。    
   
  对于大型的货物,则是拆散成火车单位,运走,取货时,依次取各部分,组装成原物。    
   
   
  2.内部数据    
   
  缓冲区指针   BYTE*   m_lpBufStart,指向缓冲区,这个缓冲区有可能是底层CFile(如派生类CMemFile)对象提供的,但一般是CArchive自己建立的。    
   
  缓冲区尾部指针   BYTE*   m_lpBufMax;    
   
  缓冲区当前位置指针   BYTE*   m_lpBufCur;    
   
  初始化时,如果是读模式,当前位置在尾部,如果是写模式,当前位置在头部:    
   
  m_lpBufCur   =   (IsLoading())   ?   m_lpBufMax   :   m_lpBufStart;    
   
   
  3.基本数据读写    
   
  对于基本的数据类型,例如字节、双字等,可以直接使用">>"、"<<"符号进行读出、写入。    
   
  //操作符定义捕:  
   
  //插入操作  
  CArchive&   operator<<(BYTE   by);  
  CArchive&   operator<<(WORD   w);  
  CArchive&   operator<<(LONG   l);  
  CArchive&   operator<<(DWORD   dw);  
  CArchive&   operator<<(float   f);  
  CArchive&   operator<<(double   d);  
  CArchive&   operator<<(int   i);  
  CArchive&   operator<<(short   w);  
  CArchive&   operator<<(char   ch);  
  CArchive&   operator<<(unsigned   u);  
   
  //提取操作  
  CArchive&   operator>>(BYTE&   by);  
  CArchive&   operator>>(WORD&   w);  
  CArchive&   operator>>(DWORD&   dw);  
  CArchive&   operator>>(LONG&   l);  
  CArchive&   operator>>(float&   f);  
  CArchive&   operator>>(double&   d);  
   
  CArchive&   operator>>(int&   i);  
  CArchive&   operator>>(short&   w);  
  CArchive&   operator>>(char&   ch);  
  CArchive&   operator>>(unsigned&   u);  
   
   
  下面以双字为例,分析原码    
   
  双字的插入(写)    
   
  CArchive&   CArchive::operator<<(DWORD   dw)  
  {  
  if   (m_lpBufCur   +   sizeof(DWORD)   >   m_lpBufMax)   //缓冲区空间不够  
  Flush();     //缓冲区内容提交到实际存储煤质。  
   
  if   (!(m_nMode   &   bNoByteSwap))  
  _AfxByteSwap(dw,   m_lpBufCur);     //处理字节顺序  
  else  
  *(DWORD*)m_lpBufCur   =   dw;             //添入缓冲区  
   
  m_lpBufCur   +=   sizeof(DWORD);         //移动当前指针  
  return   *this;  
  }  
   
  双字的提取(读)    
   
  CArchive&   CArchive::operator>>(DWORD&   dw)  
  {  
  if   (m_lpBufCur   +   sizeof(DWORD)   >   m_lpBufMax)   //缓冲区要读完了  
  FillBuffer(sizeof(DWORD)   -   (UINT)(m_lpBufMax   -   m_lpBufCur));     //重新读入内容到缓冲区  
   
  dw   =   *(DWORD*)m_lpBufCur; //读取双字  
  m_lpBufCur   +=   sizeof(DWORD); //移动当前位置指针  
   
  if   (!(m_nMode   &   bNoByteSwap))  
  _AfxByteSwap(dw,   (BYTE*)&dw);     //处理字节顺序  
  return   *this;  
  }  
   
   
  4.缓冲区的更新    
   
  以上操作中,当缓冲区将插入满或缓冲区将提取空时,都将对缓冲区进行更新处理    
   
  缓冲区将插入致满时调用Flush();    
   
  void   CArchive::Flush()  
  {  
  ASSERT_VALID(m_pFile);  
  ASSERT(m_bDirectBuffer   ||   m_lpBufStart   !=   NULL);  
  ASSERT(m_bDirectBuffer   ||   m_lpBufCur   !=   NULL);  
  ASSERT(m_lpBufStart   ==   NULL   ||  
  AfxIsValidAddress(m_lpBufStart,   m_lpBufMax   -   m_lpBufStart,   IsStoring()));  
  ASSERT(m_lpBufCur   ==   NULL   ||  
  AfxIsValidAddress(m_lpBufCur,   m_lpBufMax   -   m_lpBufCur,   IsStoring()));  
   
  if   (IsLoading())  
  {  
  //   unget   the   characters   in   the   buffer,   seek   back   unused   amount  
  if   (m_lpBufMax   !=   m_lpBufCur)  
  m_pFile->   Seek(-(m_lpBufMax   -   m_lpBufCur),   CFile::current);  
  m_lpBufCur   =   m_lpBufMax;         //   指向尾  
  }  
  else       //写模式  
  {  
  if   (!m_bDirectBuffer)  
  {  
  //   内容写入到文件  
  if   (m_lpBufCur   !=   m_lpBufStart)  
  m_pFile->   Write(m_lpBufStart,   m_lpBufCur   -   m_lpBufStart);  
  }  
  else  
  {  
  //如果是直接针对内存区域的的(例如CMemFile中)   (只需移动相关指针,指向新的一块内存)  
  if   (m_lpBufCur   !=   m_lpBufStart)  
  m_pFile->   GetBufferPtr(CFile::bufferCommit,   m_lpBufCur   -   m_lpBufStart);  
  //   get   next   buffer  
  VERIFY(m_pFile->   GetBufferPtr(CFile::bufferWrite,   m_nBufSize,  
  (void**)&m_lpBufStart,   (void**)&m_lpBufMax)   ==   (UINT)m_nBufSize);  
  ASSERT((UINT)m_nBufSize   ==   (UINT)(m_lpBufMax   -   m_lpBufStart));  
  }  
  m_lpBufCur   =   m_lpBufStart;   //指向缓冲区首  
  }  
  }  
   
   
  Top

2 楼free_card(痛并快乐着)回复于 2003-04-03 11:09:18 得分 0

 
  缓冲区将提取空将调用GFileeBuffer,nBytesNeeded为当前剩余部分上尚有用的字节    
   
  void   CArchive::FillBuffer(UINT   nBytesNeeded)  
  {  
  ASSERT_VALID(m_pFile);  
  ASSERT(IsLoading());  
  ASSERT(m_bDirectBuffer   ||   m_lpBufStart   !=   NULL);  
  ASSERT(m_bDirectBuffer   ||   m_lpBufCur   !=   NULL);  
  ASSERT(nBytesNeeded   >   0);  
  ASSERT(nBytesNeeded   <=   (UINT)m_nBufSize);  
  ASSERT(m_lpBufStart   ==   NULL   ||  
  AfxIsValidAddress(m_lpBufStart,   m_lpBufMax   -   m_lpBufStart,   FALSE));  
  ASSERT(m_lpBufCur   ==   NULL   ||  
  AfxIsValidAddress(m_lpBufCur,   m_lpBufMax   -   m_lpBufCur,   FALSE));  
   
  UINT   nUnused   =   m_lpBufMax   -   m_lpBufCur;  
  ULONG   nTotalNeeded   =   ((ULONG)nBytesNeeded)   +   nUnused;  
   
  //   从文件中读取  
  if   (!m_bDirectBuffer)  
  {  
  ASSERT(m_lpBufCur   !=   NULL);  
  ASSERT(m_lpBufStart   !=   NULL);  
  ASSERT(m_lpBufMax   !=   NULL);  
   
  if   (m_lpBufCur   >   m_lpBufStart)  
  {  
  //保留剩余的尚未处理的部分,将它们移动到头  
  if   ((int)nUnused   >   0)  
  {  
  memmove(m_lpBufStart,   m_lpBufCur,   nUnused);  
  m_lpBufCur   =   m_lpBufStart;  
  m_lpBufMax   =   m_lpBufStart   +   nUnused;  
  }  
   
  //   read   to   satisfy   nBytesNeeded   or   nLeft   if   possible  
  UINT   nRead   =   nUnused;  
  UINT   nLeft   =   m_nBufSize-nUnused;  
  UINT   nBytes;  
  BYTE*   lpTemp   =   m_lpBufStart   +   nUnused;  
  do  
  {  
  nBytes   =   m_pFile->   Read(lpTemp,   nLeft);  
  lpTemp   =   lpTemp   +   nBytes;  
  nRead   +=   nBytes;  
  nLeft   -=   nBytes;  
  }  
  while   (nBytes   >   0   &&   nLeft   >   0   &&   nRead   <   nBytesNeeded);  
   
  m_lpBufCur   =   m_lpBufStart;  
  m_lpBufMax   =   m_lpBufStart   +   nRead;  
  }  
  }  
  else  
  {  
  //   如果是针对内存区域(CMemFile),移动相关指针,指向新的一块内存  
  if   (nUnused   !=   0)  
  m_pFile->   Seek(-(LONG)nUnused,   CFile::current);  
  UINT   nActual   =   m_pFile->   GetBufferPtr(CFile::bufferRead,   m_nBufSize,  
  (void**)&m_lpBufStart,   (void**)&m_lpBufMax);  
  ASSERT(nActual   ==   (UINT)(m_lpBufMax   -   m_lpBufStart));  
  m_lpBufCur   =   m_lpBufStart;  
  }  
   
  //   not   enough   data   to   fill   request?  
  if   ((ULONG)(m_lpBufMax   -   m_lpBufCur)   <   nTotalNeeded)  
  AfxThrowArchiveException(CArchiveException::endOfFile);  
  }  
   
   
  5.指定长度数据段落的读写    
   
  以下分析    
   
  UINT   Read(void*   lpBuf,   UINT   nMax);   读取长度为nMax的数据    
   
  void   Write(const   void*   lpBuf,   UINT   nMax);   写入指定长度nMax的数据    
   
  对于大段数据的读写,先使用当前缓冲区中的内容或空间读取或写入,若这些空间够用了,则结束。    
   
  否则,从剩余的数据中找出最大的缓冲区整数倍大小的一块数据,直接读写到存储煤质(不反复使用缓冲区)。    
   
  剩余的余数部分,再使用缓冲区读写。    
   
  (说明:缓冲区读写的主要目的是将零散的数据以缓冲区大小为尺度来处理。对于大型数据,其中间的部分,不是零散的数据,使用缓冲区已经没有意思,故直接读写)    
   
  ①读取    
   
  UINT   CArchive::Read(void*   lpBuf,   UINT   nMax)  
  {  
  ASSERT_VALID(m_pFile);  
  if   (nMax   ==   0)  
  return   0;  
   
  UINT   nMaxTemp   =   nMax;     //还需要读入的长度,读入一部分,就减相应数值,直到此数值变为零  
   
  //处理当前缓冲区中剩余部分。  
  //如果要求读入字节小于缓冲区中剩余部分,则第一部分为要求读入的字节数,  
  //否则读入全部剩余部分  
  UINT   nTemp   =   min(nMaxTemp,   (UINT)(m_lpBufMax   -   m_lpBufCur));        
  memcpy(lpBuf,   m_lpBufCur,   nTemp);  
  m_lpBufCur   +=   nTemp;  
  lpBuf   =   (BYTE*)lpBuf   +   nTemp;   //移动读出内容所在区域的指针  
  nMaxTemp   -=   nTemp;  
   
  //当前缓冲区中剩余部分不够要求读入的长度。  
  //还有字节需要读,则需要根据需要执行若干次填充缓冲区,读出,直到读出指定字节。  
  if   (nMaxTemp   !=   0)      
  {  
  //计算出去除尾数部分的字节大小(整数个缓冲区大小)    
  //对于这些部分,字节从文件对象中读出,放到输出缓冲区  
  nTemp   =   nMaxTemp   -   (nMaxTemp   %   m_nBufSize);      
  UINT   nRead   =   0;  
   
  UINT   nLeft   =   nTemp;  
  UINT   nBytes;  
  do  
  {  
  nBytes   =   m_pFile->   Read(lpBuf,   nLeft);   //要求读入此整数缓冲区部分大小  
  lpBuf   =   (BYTE*)lpBuf   +   nBytes;  
  nRead   +=   nBytes;  
  nLeft   -=   nBytes;  
  }  
  while   ((nBytes   >   0)   &&   (nLeft   >   0));   知道读入了预定大小,或到达文件尾  
   
  nMaxTemp   -=   nRead;  
   
  if   (nRead   ==   nTemp)   //读入的字节等于读入的整数倍部分     该读最后的余数部分了  
  {  
  //   建立装有此最后余数部分的内容的CArchive的工作缓冲区。  
  if   (!m_bDirectBuffer)  
  {  
  UINT   nLeft   =   max(nMaxTemp,   (UINT)m_nBufSize);  
  UINT   nBytes;  
  BYTE*   lpTemp   =   m_lpBufStart;  
  nRead   =   0;  
  do  
  {  
  nBytes   =   m_pFile->   Read(lpTemp,   nLeft);     //从文件中读入到CArchive缓冲区  
  lpTemp   =   lpTemp   +   nBytes;  
  nRead   +=   nBytes;  
  nLeft   -=   nBytes;  
  }  
  while   ((nBytes   >   0)   &&   (nLeft   >   0)   &&   nRead   <   nMaxTemp);  
   
  m_lpBufCur   =   m_lpBufStart;  
  m_lpBufMax   =   m_lpBufStart   +   nRead;  
  }  
  else  
  {  
  nRead   =   m_pFile->   GetBufferPtr(CFile::bufferRead,   m_nBufSize,  
  (void**)&m_lpBufStart,   (void**)&m_lpBufMax);  
  ASSERT(nRead   ==   (UINT)(m_lpBufMax   -   m_lpBufStart));  
  m_lpBufCur   =   m_lpBufStart;  
  }  
   
  //读出此剩余部分到输出  
  nTemp   =   min(nMaxTemp,   (UINT)(m_lpBufMax   -   m_lpBufCur));  
  memcpy(lpBuf,   m_lpBufCur,   nTemp);  
  m_lpBufCur   +=   nTemp;  
  nMaxTemp   -=   nTemp;  
  }  
   
  }  
  return   nMax   -   nMaxTemp;  
  }  
   
  ②保存,写入    
   
  void   CArchive::Write(const   void*   lpBuf,   UINT   nMax)  
  {  
  if   (nMax   ==   0)  
  return;  
   
  //读入可能的部分到缓冲区当前的剩余部分  
  UINT   nTemp   =   min(nMax,   (UINT)(m_lpBufMax   -   m_lpBufCur));  
  memcpy(m_lpBufCur,   lpBuf,   nTemp);  
  m_lpBufCur   +=   nTemp;  
  lpBuf   =   (BYTE*)lpBuf   +   nTemp;  
  nMax   -=   nTemp;  
   
  if   (nMax   >   0)     //还有未写入的部分  
  {  
  Flush();         //将当前缓冲区写入到存储煤质  
   
  //计算出整数倍缓冲区大小的字节数  
  nTemp   =   nMax   -   (nMax   %   m_nBufSize);  
  m_pFile->   Write(lpBuf,   nTemp);     //直接写到文件  
  lpBuf   =   (BYTE*)lpBuf   +   nTemp;  
  nMax   -=   nTemp;  
   
   
  //剩余部分添加到缓冲区  
  if   (m_bDirectBuffer)  
  {  
  //   sync   up   direct   mode   buffer   to   new   file   position  
  VERIFY(m_pFile->   GetBufferPtr(CFile::bufferWrite,   m_nBufSize,  
  (void**)&m_lpBufStart,   (void**)&m_lpBufMax)   ==   (UINT)m_nBufSize);  
  ASSERT((UINT)m_nBufSize   ==   (UINT)(m_lpBufMax   -   m_lpBufStart));  
  m_lpBufCur   =   m_lpBufStart;  
  }  
   
  //   copy   remaining   to   active   buffer  
  ASSERT(nMax   <   (UINT)m_nBufSize);  
  ASSERT(m_lpBufCur   ==   m_lpBufStart);  
  memcpy(m_lpBufCur,   lpBuf,   nMax);  
  m_lpBufCur   +=   nMax;  
  }  
  }  
   
  6.字符串的读写    
   
  ①CArchive提供的WriteString和ReadString    
   
  字符串写    
   
  void   CArchive::WriteString(LPCTSTR   lpsz)  
  {  
  ASSERT(AfxIsValidString(lpsz));  
  Write(lpsz,   lstrlen(lpsz)   *   sizeof(TCHAR));     //调用Write,将字符串对应的一段数据写入  
  }  
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值