精华内容
下载资源
问答
  • CArchive

    2013-03-02 11:28:23
    来自:... ...CArchive ...我向你推荐的是使用CArchive,它的使用方法简单且功能十分强大。首先还是用CFile声明一个对象,然后用这个对象的指针做参数声明一个CA

     来自:http://blog.sina.com.cn/s/blog_4ae1ad2001000bqd.html


    1 CArchive
     
      虽然这种方法最为基本,但是它的使用繁琐,而且功能非常简单。我向你推荐的是使用CArchive,它的使用方法简单且功能十分强大。首先还是用CFile声明一个对象,然后用这个对象的指针做参数声明一个CArchive对象,你就可以非常方便地存储各种复杂的数据类型了。它的使用方法见下例。 
      //对文件进行写操作 
      CString strTemp; 
      CFile mFile; 
      mFile.Open("d:\\dd\\try.TRY",CFile::modeCreate|CFile::modeNoTruncate|CFile::modeWrite); 
      CArchive ar(&mFile,CArchive::store); 
      ar<<  ar.Close(); 
      mFile.Close(); 
      //对文件进行读操作 
      CFile mFile; 
      if(mFile.Open("d:\\dd\\try.TRY",CFile::modeRead)==0) 
      return; 
      CArchive ar(&mFile,CArchive::load); 
       ar>>strTemp; 
          ar.Close(); 
      mFile.Close(); 
      CArchive的 << 和>> 操作符用于简单数据类型的读写,对于CObject派生类的对象的存取要使用ReadObject()和WriteObject()。使用CArchive的ReadClass()和WriteClass()还可以进行类的读写,如: 
      //存储CAboutDlg类 
      ar.WriteClass(RUNTIME_CLASS(CAboutDlg)); 
      //读取CAboutDlg类 
      CRuntimeClass* mRunClass=ar.ReadClass(); 
      //使用CAboutDlg类 
      CObject* pObject=mRunClass->CreateObject(); 
          ((CDialog* )pObject)->DoModal(); 
      虽然VC提供的文档/视结构中的文档也可进行这些操作,但是不容易理解、使用和管理,因此虽然很多VC入门的书上花费大量篇幅讲述文档/视结构,但我建议你最好不要使用它的文档。关于如何进行文档/视的分离有很多书介绍,包括非常著名的《Visual C++ 技术内幕》。 
      如果你要进行的文件操作只是简单的读写整行的字符串,我建议你使用CStdioFile,用它来进行此类操作非常方便,如下例。 
      CStdioFile mFile; 
      CFileException mExcept; 
      mFile.Open( "d:\\temp\\aa.bat", CFile::modeWrite, &mExcept); 
      CString string="I am a string."; 
      mFile.WriteString(string); 
      mFile.Close(); 
     4.临时文件的使用 

      正规软件经常用到临时文件,你经常可以会看到C:\Windows\Temp目录下有大量的扩展名为tmp的文件,这些就是程序运行是建立的临时文件。临时文件的使用方法基本与常规文件一样,只是文件名应该调用函数GetTempFileName()获得。它的第一个参数是建立此临时文件的路径,第二个参数是建立临时文件名的前缀,第四个参数用于得到建立的临时文件名。得到此临时文件名以后,你就可以用它来建立并操作文件了,如: 
      char szTempPath[_MAX_PATH],szTempfile[_MAX_PATH]; 
      GetTempPath(_MAX_PATH, szTempPath); 
      GetTempFileName(szTempPath,_T ("my_"),0,szTempfile); 
      CFile m_tempFile(szTempfile,CFile:: modeCreate|CFile:: modeWrite); 
      char m_char='a'; 
      m_tempFile.Write(&m_char,2); 
      m_tempFile.Close(); 
      5.文件的复制、删除等 
      MFC中没有提供直接进行这些操作的功能,因而要使用SDK。SDK中的文件相关函数常用的有CopyFile()、CreateDirectory()、DeleteFile()、MoveFile()。它们的用法很简单,可参考MSDN。 

    1,判断文件是否存在
        access(filename,mode);
    2,对于不同用途又不同的文件操作,其中API函数CreateFile()也是比较有用处理方式,对于巨型文件很合适的其他的楼上的大都说了,不重复了.

    [1]显示对话框,取得文件名

    CString FilePathName;
    CFileDialog dlg(TRUE);///TRUE为OPEN对话框,FALSE为S***E AS对话框
    if (dlg.DoModal() == IDOK)
        FilePathName=dlg.GetPathName();

    相关信息:CFileDialog 用于取文件名的几个成员函数:
    假如选择的文件是C:\WINDOWS\TEST.EXE
    则(1)GetPathName();取文件名全称,包括完整路径。取回C:\WINDOWS\TEST.EXE
    (2)GetFileTitle();取文件全名:TEST.EXE
    (3)GetFileName();取回TEST
    (4)GetFileExt();取扩展名EXE

    [2]打开文件
    CFile file("C:\HELLO.TXT",CFile::modeRead);//只读方式打开
    //CFile::modeRead可改为 CFile::modeWrite(只写),
    //CFile::modeReadWrite(读写),CFile::modeCreate(新建)
    例子:
    {
    CFile file;
    file.Open("C:\HELLO.TXT",CFile::modeCreate|Cfile::modeWrite);
    .
    .
    .
    }

    [3]移动文件指针
    file.Seek(100,CFile::begin);///从文件头开始往下移动100字节
    file.Seek(-50,CFile::end);///从文件末尾往上移动50字节
    file.Seek(-30,CFile::current);///从当前位置往上移动30字节
    file.SeekToBegin();///移到文件头
    file.SeekToEnd();///移到文件尾

    [4]读写文件
    读文件:
    char buffer[1000];
    file.Read(buffer,1000);
    写文件:
    CString string("自强不息");
    file.Write(string,8);

    [5]关闭文件
    file.Close(); 

    展开全文
  • CArchive实例

    2009-07-16 09:01:24
    关于自己对CArchive的理解,希望能有所帮助!
  • CArchive 详解

    2016-08-01 13:49:44
    CArchive没有基类 CArchive允许以一个永久二进制(通常为磁盘存储)的形式保存一个对象的复杂网络,它可以在对象被删除时,还能永久保存。可以从永久存储中装载对象,在内存中重新构造它们。使得数据永久保留的...

    CArchive没有基类

    CArchive允许以一个永久二进制(通常为磁盘存储)的形式保存一个对象的复杂网络,它可以在对象被删除时,还能永久保存。可以从永久存储中装载对象,在内存中重新构造它们。使得数据永久保留的过程就叫作“串行化”。
    可以把一个归档对象看作一种二进制流。像输入/输出流一样,归档与文件有关并允许写缓冲区以及从硬盘读出或读入数据。输入/输出流处理一系列ASCII字符,但是归档文件以一种有效率、精练的格式处理二进制对象。
    必须在创建一个CArchive对象之前,创建一个CFile对象。另外,必须确信归档文件的装入/存储与文件的打开模式是兼容的。每一个文件只限于一个活动归档文件。
    当构造一个CArchive对象时,要把它附加给表示一个打开文件的类CFile(或派生类)的对象上。还要指定归档文件将用于装载还是存储。CArchive对象不仅可以处理首要类型,而且还能处理为串行化而设计的CObject_派生类的对象。一个串行化类通常有一个Serialize成员函数并且使用DECLARE_SERIALIMPLEMENT_SERIAL宏。这些在CObject类中有所描述。
    重载提取(>>)和插入(<<)是方便的归档编程接口。它支持主要类型和CObject派生类。
    CArchive还支持使用MFC Windows套接字CSocketCSocketFile编程。IsBufferEmpty成员函数也支持这种使用。如果要了解有关CArchive的更多信息,请参阅联机文档“Visual C++ 程序员指南”中的“串行化(永久对象)” 和“Windows套接字:在归档文件中使用套接字”
    include<afx.h>
    请参阅 CFile,CObject,CSocket,CSocketFile
    展开全文
  • CArchive体会

    千次阅读 2015-01-09 20:35:55
    第一次使用CFile和CArchive读取txt文件时,所遇到的问题: 直接读取一个已经存在的文件时,用CArchive::>>CString无法读取整行的字符串,所读取的字符串超过了一个整行,而且其中有空格及回车符等等。 当换用...

    第一次使用CFile和CArchive读取txt文件时,所遇到的问题:  直接读取一个已经存在的文件时,用CArchive::>>CString无法读取整行的字符串,所读取的字符串超过了一个整行,而且其中有空格及回车符等等。  当换用CArchive::ReadString(CString)时,读取的是乱码。


    当面对这个问题时,我也很烦躁。


    浏览了一些blog后,得到的信息如下:

    ①CArchive可以读取由CArchive写入的txt字符串

    ②CArchive读CString时,第一个字符会被处理为的长度而不是读一行

    ③貌似可以使用CStdioFile::ReadString来读取一行。


    从txt中读取的字符,如果是多字节格式(MultiByte)的,读入CString后会形成乱码,需要用MultiByteToWideChar转换为宽字节(WideCode,Unicode)格式。


    一个小例子:

    CString对象str从File中读取了所有的字符(此处本想只读一行的,但没有实现掉),之后转为Unicode,最后用wcstok_s来分隔。

    CFile file(dlg_open.GetPathName(), CFile::modeRead);
    CArchive ar(&file,CArchive::load);
    m_map = new Map;

    CString  str;
    ar.ReadString(str);

    size_t nchars = MultiByteToWideChar(CP_ACP, 0, (LPSTR)(LPCTSTR)str, -1, 0, 0);
    wchar_t * pUnicode;
    pUnicode = new wchar_t[nchars + 1];
    memset(pUnicode, 0, (nchars + 1)*sizeof(wchar_t));
    MultiByteToWideChar(CP_ACP, 0, (LPSTR)(LPCTSTR)str, -1, (LPWSTR)pUnicode, nchars);


    wchar_t delim = _T('\n');
    wchar_t *next_token1 = NULL;


    wchar_t* p = wcstok_s(pUnicode, &delim, &next_token1);

    while (p != 0)
    {
    p = wcstok_s(0, &delim, &next_token1);


    }

    展开全文
  • C++CArchive序列化存储

    2018-10-17 23:39:47
    CArchive类的简单用法,实现C++序列化和反序列化存储,简单明了
  • CArchive 原理

    2012-05-16 15:07:06
    CArchive 原理  一.概述 CArchive使用了缓冲区,即一段内存空间作为临时数据存储地,对CArchive的读写都先依次排列到此缓冲区,当缓冲区满或用户要求时,将此段整理后的数据读写到指定的存储煤质。  当建立...

    CArchive 原理 

    一.概述
    CArchive使用了缓冲区,即一段内存空间作为临时数据存储地,对CArchive的读写都先依次排列到此缓冲区,当缓冲区满或用户要求时,将此段整理后的数据读写到指定的存储煤质。 
    当建立CArchive对象时,应指定其模式是用于缓冲区读,还是用于缓冲区写。 
    可以这样理解,CArchive对象相当于铁路的货运练调度站,零散的货物被收集,当总量到达火车运量的时候,由火车装运走。 
    当接到火车的货物时,则货物由被分散到各自的货主。与货运不同的是,交货、取货是按时间循序执行的,而不是凭票据。因此必须保证送货的和取货的货主按同样的循序去存或取。 

    对于大型的货物,则是拆散成火车单位,运走,取货时,依次取各部分,组装成原物。

    二.内部数据
    缓冲区指针 BYTE* m_lpBufStart,指向缓冲区,这个缓冲区有可能是底层CFile(如派生类CMemFile)对象提供的,但一般是CArchive自己建立的。 
    缓冲区尾部指针 BYTE* m_lpBufMax; 
    缓冲区当前位置指针 BYTE* m_lpBufCur; 
    初始化时,如果是读模式,当前位置在尾部,如果是写模式,当前位置在头部:

    m_lpBufCur = (IsLoading()) ? m_lpBufMax : m_lpBufStart;

    三.基本数据读写
    对于基本的数据类型,例如字节、双字等,可以直接使用">>"、"<<"符号进行读出、写入。

    //操作符定义捕:
    //插入操作
    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;
    }
    四.缓冲区的更新
    以上操作中,当缓冲区将插入满或缓冲区将提取空时,都将对缓冲区进行更新处理。

    缓冲区将插入满时调用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; //指向缓冲区首
    	}
    }
    //缓冲区将提取空,会调用FillBuffer。 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);
    }

    五.指定长度数据段落的读写
    以下分析 
    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;
    	}
    }

    六.字符串的读写
    ①CArchive提供的WriteString和ReadString 

    字符串写
    void CArchive::WriteString(LPCTSTR lpsz)
    {
    ASSERT(AfxIsValidString(lpsz));
    Write(lpsz, lstrlen(lpsz) * sizeof(TCHAR));  //调用Write,将字符串对应的一段数据写入
    }
    字符串读(读取一行字符串)
    LPTSTR CArchive::ReadString(LPTSTR lpsz, UINT nMax)
    {
    	// if nMax is negative (such a large number doesn''t make sense given today''s
    	// 2gb address space), then assume it to mean "keep the newline".
    	int nStop = (int)nMax < 0 ? -(int)nMax : (int)nMax;
    	ASSERT(AfxIsValidAddress(lpsz, (nStop+1) * sizeof(TCHAR)));
    
    	_TUCHAR ch;
    	int nRead = 0;
    
    	TRY
    	{
    		while (nRead < nStop)
    		{
    			*this >> ch;  //读出一个字节
    
    			// stop and end-of-line (trailing ''\n'' is ignored)  遇换行—回车
    			if (ch == ''\n'' || ch == ''\r'')
    			{
    				if (ch == ''\r'')
    					*this >> ch;
    				// store the newline when called with negative nMax
    				if ((int)nMax != nStop)
    					lpsz[nRead++] = ch;
    				break;
    			}
    			lpsz[nRead++] = ch;
    		}
    	}
    	CATCH(CArchiveException, e)
    	{
    		if (e-> m_cause == CArchiveException::endOfFile)
    		{
    			DELETE_EXCEPTION(e);
    			if (nRead == 0)
    				return NULL;
    		}
    		else
    		{
    			THROW_LAST();
    		}
    	}
    	END_CATCH
    		lpsz[nRead] = ''\0'';
    	return lpsz;
    }

    ReadString到CString对象,可以多行字符
    BOOL CArchive::ReadString(CString& rString)
    {
    	rString = &afxChNil;    // empty string without deallocating
    	const int nMaxSize = 128;
    	LPTSTR lpsz = rString.GetBuffer(nMaxSize);
    	LPTSTR lpszResult;
    	int nLen;
    	for (;;)
    	{
    		lpszResult = ReadString(lpsz, (UINT)-nMaxSize); // store the newline
    		rString.ReleaseBuffer();
    
    		// if string is read completely or EOF
    		if (lpszResult == NULL || (nLen = lstrlen(lpsz)) < nMaxSize || lpsz[nLen-1] == ''\n'')
    		{
    			break;
    		}
    
    		nLen = rString.GetLength();
    		lpsz = rString.GetBuffer(nMaxSize + nLen) + nLen;
    	}
    
    	// remove ''\n'' from end of string if present
    	lpsz = rString.GetBuffer(0);
    	nLen = rString.GetLength();
    	if (nLen != 0 && lpsz[nLen-1] == ''\n'')
    		rString.GetBufferSetLength(nLen-1);
    
    	return lpszResult != NULL;
    }

    ②使用CString对象的"<<"与">>"符读写字符串
    CString定义了输入输出符,可以象基本类型的数据一样使用CArchive 的操作符定义
    friend CArchive& AFXAPI operator<<(CArchive& ar, const CString& string);
    friend CArchive& AFXAPI operator>>(CArchive& ar, CString& string);
    // CString serialization code
    // String format:
    //      UNICODE strings are always prefixed by 0xff, 0xfffe
    //      if < 0xff chars: len:BYTE, TCHAR chars
    //      if >= 0xff characters: 0xff, len:WORD, TCHAR chars
    //      if >= 0xfffe characters: 0xff, 0xffff, len:DWORD, TCHARs
    
    
    CArchive& AFXAPI operator<<(CArchive& ar, const CString& string)
    {
    	// special signature to recognize unicode strings
    #ifdef _UNICODE
    	ar << (BYTE)0xff;
    	ar << (WORD)0xfffe;
    #endif
    
    
    	if (string.GetData()-> nDataLength < 255)
    	{
    		ar << (BYTE)string.GetData()-> nDataLength;
    	}
    	else if (string.GetData()-> nDataLength < 0xfffe)
    	{
    		ar << (BYTE)0xff;
    		ar << (WORD)string.GetData()-> nDataLength;
    	}
    	else
    	{
    		ar << (BYTE)0xff;
    		ar << (WORD)0xffff;
    		ar << (DWORD)string.GetData()-> nDataLength;
    	}
    	ar.Write(string.m_pchData, string.GetData()-> nDataLength*sizeof(TCHAR));
    	return ar;
    }
    
    
    // return string length or -1 if UNICODE string is found in the archive
    AFX_STATIC UINT AFXAPI _AfxReadStringLength(CArchive& ar)
    {
    	DWORD nNewLen;
    
    
    	// attempt BYTE length first
    	BYTE bLen;
    	ar >> bLen;
    
    
    	if (bLen < 0xff)
    		return bLen;
    
    
    	// attempt WORD length
    	WORD wLen;
    	ar >> wLen;
    	if (wLen == 0xfffe)
    	{
    		// UNICODE string prefix (length will follow)
    		return (UINT)-1;
    	}
    	else if (wLen == 0xffff)
    	{
    		// read DWORD of length
    		ar >> nNewLen;
    		return (UINT)nNewLen;
    	}
    	else
    		return wLen;
    }
    
    
    CArchive& AFXAPI operator>>(CArchive& ar, CString& string)
    {
    #ifdef _UNICODE
    	int nConvert = 1;   // if we get ANSI, convert
    #else
    	int nConvert = 0;   // if we get UNICODE, convert
    #endif
    
    
    	UINT nNewLen = _AfxReadStringLength(ar);
    	if (nNewLen == (UINT)-1)
    	{
    		nConvert = 1 - nConvert;
    		nNewLen = _AfxReadStringLength(ar);
    		ASSERT(nNewLen != -1);
    	}
    
    
    	// set length of string to new length
    	UINT nByteLen = nNewLen;
    #ifdef _UNICODE
    	string.GetBufferSetLength((int)nNewLen);
    	nByteLen += nByteLen * (1 - nConvert);  // bytes to read
    #else
    	nByteLen += nByteLen * nConvert;    // bytes to read
    	if (nNewLen == 0)
    		string.GetBufferSetLength(0);
    	else
    		string.GetBufferSetLength((int)nByteLen+nConvert);
    #endif
    
    
    	// read in the characters
    	if (nNewLen != 0)
    	{
    		ASSERT(nByteLen != 0);
    
    
    		// read new data
    		if (ar.Read(string.m_pchData, nByteLen) != nByteLen)
    			AfxThrowArchiveException(CArchiveException::endOfFile);
    
    
    		// convert the data if as necessary
    		if (nConvert != 0)
    		{
    #ifdef _UNICODE
    			CStringData* pOldData = string.GetData();
    			LPSTR lpsz = (LPSTR)string.m_pchData;
    #else
    			CStringData* pOldData = string.GetData();
    			LPWSTR lpsz = (LPWSTR)string.m_pchData;
    #endif
    			lpsz[nNewLen] = ''\0'';    // must be NUL terminated
    			string.Init();   // don''t delete the old data
    			string = lpsz;   // convert with operator=(LPWCSTR)
    			CString::FreeData(pOldData);
    		}
    	}
    	return ar;
    }

    七.CObject派生对象的读写
    MFC中多数类都从CObject类派生,CObject类与CArchive类有着良好的合作关系,能实现将对象序列化储存到文件或其他媒介中去,或者读取预先储存的对象,动态建立对象等功能。

    ①CObject定义了针对CArvhive的输入输出操作符,可以向其他基本数据类型一样使用"<<"、"<<"符号

    CArchive& AFXAPI operator<<(CArchive& ar, const CObject* pOb)
    { ar.WriteObject(pOb); return ar; }
    CArchive& AFXAPI operator>>(CArchive& ar, CObject*& pOb)
    { pOb = ar.ReadObject(NULL); return ar; }
    当使用这些符号时,实际上执行的是CArchive的WriteObject和ReadObject成员
    ②WriteObject与ReadObject

    在WriteObject与ReadObject中先写入或读取运行时类信息(CRuntimeClas),再调用Serialze(..),按其中的代码读写具体的对象数据。

    因此,只要在CObject派生类中重载Serilize()函数,写入具体的读写过程,就可以使对象具有存储与创建能力。
    //将对象写入到缓冲区
    void CArchive::WriteObject(const CObject* pOb)
    {
    	DWORD nObIndex;
    	// make sure m_pStoreMap is initialized
    	MapObject(NULL);
    
    	if (pOb == NULL)
    	{
    		// save out null tag to represent NULL pointer
    		*this << wNullTag;
    	}
    	else if ((nObIndex = (DWORD)(*m_pStoreMap)[(void*)pOb]) != 0)
    		// assumes initialized to 0 map
    	{
    		// save out index of already stored object
    		if (nObIndex < wBigObjectTag)
    			*this << (WORD)nObIndex;
    		else
    		{
    			*this << wBigObjectTag;
    			*this << nObIndex;
    		}
    	}
    	else
    	{
    		// write class of object first
    		CRuntimeClass* pClassRef = pOb-> GetRuntimeClass();
    		WriteClass(pClassRef);  //写入运行类信息
    
    		// enter in stored object table, checking for overflow
    		CheckCount();
    		(*m_pStoreMap)[(void*)pOb] = (void*)m_nMapCount++;
    
    		// 调用CObject的Serialize成员,按其中的代码写入类中数据。
    		((CObject*)pOb)-> Serialize(*this);
    	}
    }
    
    CObject* CArchive::ReadObject(const CRuntimeClass* pClassRefRequested)
    {
    	// attempt to load next stream as CRuntimeClass
    	UINT nSchema;
    	DWORD obTag;
    	//先读入运行时类信息
    	CRuntimeClass* pClassRef = ReadClass(pClassRefRequested, &nSchema, &obTag);
    
    	// check to see if tag to already loaded object
    	CObject* pOb;
    	if (pClassRef == NULL)
    	{
    		if (obTag > (DWORD)m_pLoadArray-> GetUpperBound())
    		{
    			// tag is too large for the number of objects read so far
    			AfxThrowArchiveException(CArchiveException::badIndex,
    				m_strFileName);
    		}
    
    		pOb = (CObject*)m_pLoadArray-> GetAt(obTag);
    		if (pOb != NULL && pClassRefRequested != NULL &&
    			!pOb-> IsKindOf(pClassRefRequested))
    		{
    			// loaded an object but of the wrong class
    			AfxThrowArchiveException(CArchiveException::badClass,
    				m_strFileName);
    		}
    	}
    	else
    	{
    		// 建立对象
    		pOb = pClassRef-> CreateObject();
    		if (pOb == NULL)
    			AfxThrowMemoryException();
    
    		// Add to mapping array BEFORE de-serializing
    		CheckCount();
    		m_pLoadArray-> InsertAt(m_nMapCount++, pOb);
    
    		// Serialize the object with the schema number set in the archive
    		UINT nSchemaSave = m_nObjectSchema;
    		m_nObjectSchema = nSchema;
    		pOb-> Serialize(*this); //调用CObject的Serialize,按其中代码读入对象数据。
    		m_nObjectSchema = nSchemaSave;
    		ASSERT_VALID(pOb);
    	}
    
    	return pOb;
    }

    ③运行时类信息的读写
    为了避免众多重复的同类对象写入重复的类信息,CArchive中使用CMap对象储存和检索类信息。
    void CArchive::WriteClass(const CRuntimeClass* pClassRef)
    {
    	ASSERT(pClassRef != NULL);
    	ASSERT(IsStoring());    // proper direction
    
    	if (pClassRef-> m_wSchema == 0xFFFF)
    	{
    		TRACE1("Warning: Cannot call WriteClass/WriteObject for %hs.\n", pClassRef-> m_lpszClassName);
    		AfxThrowNotSupportedException();
    	}
    
    	// make sure m_pStoreMap is initialized
    	MapObject(NULL);
    
    	// write out class id of pOb, with high bit set to indicate
    	// new object follows
    
    	// ASSUME: initialized to 0 map
    	DWORD nClassIndex;
    	if ((nClassIndex = (DWORD)(*m_pStoreMap)[(void*)pClassRef]) != 0)
    	{
    		// previously seen class, write out the index tagged by high bit
    		if (nClassIndex < wBigObjectTag)
    		{
    			*this << (WORD)(wClassTag | nClassIndex);
    		}
    		else
    		{
    			*this << wBigObjectTag;
    			*this << (dwBigClassTag | nClassIndex);
    		}
    	}
    	else
    	{
    		// store new class
    		*this << wNewClassTag;
    		pClassRef-> Store(*this);
    
    		// store new class reference in map, checking for overflow
    		CheckCount();
    		(*m_pStoreMap)[(void*)pClassRef] = (void*)m_nMapCount++;
    	}
    }
    
    
    CRuntimeClass* CArchive::ReadClass(const CRuntimeClass* pClassRefRequested,
    	UINT* pSchema, DWORD* pObTag)
    {
    	ASSERT(pClassRefRequested == NULL || AfxIsValidAddress(pClassRefRequested, sizeof(CRuntimeClass), FALSE));
    	ASSERT(IsLoading());    // proper direction
    
    	if (pClassRefRequested != NULL && pClassRefRequested-> m_wSchema == 0xFFFF)
    	{
    		TRACE1("Warning: Cannot call ReadClass/ReadObject for %hs.\n",
    			pClassRefRequested-> m_lpszClassName);
    		AfxThrowNotSupportedException();
    	}
    
    	// make sure m_pLoadArray is initialized
    	MapObject(NULL);
    
    	// read object tag - if prefixed by wBigObjectTag then DWORD tag follows
    	DWORD obTag;
    	WORD wTag;
    	*this >> wTag;
    	if (wTag == wBigObjectTag)
    	{
    		*this >> obTag;
    	}
    	else
    	{
    		obTag = ((wTag & wClassTag) << 16) | (wTag & ~wClassTag);
    	}
    
    
    	// check for object tag (throw exception if expecting class tag)
    	if (!(obTag & dwBigClassTag))
    	{
    		if (pObTag == NULL)
    			AfxThrowArchiveException(CArchiveException::badIndex, m_strFileName);
    
    		*pObTag = obTag;
    		return NULL;
    	}
    
    	CRuntimeClass* pClassRef;
    	UINT nSchema;
    	if (wTag == wNewClassTag)
    	{
    		// new object follows a new class id
    		if ((pClassRef = CRuntimeClass::Load(*this, &nSchema)) == NULL)
    			AfxThrowArchiveException(CArchiveException::badClass, m_strFileName);
    
    		// check nSchema against the expected schema
    		if ((pClassRef-> m_wSchema & ~VERSIONABLE_SCHEMA) != nSchema)
    		{
    			if (!(pClassRef-> m_wSchema & VERSIONABLE_SCHEMA))
    			{
    				// schema doesn''t match and not marked as VERSIONABLE_SCHEMA
    				AfxThrowArchiveException(CArchiveException::badSchema,
    					m_strFileName);
    			}
    			else
    			{
    				// they differ -- store the schema for later retrieval
    				if (m_pSchemaMap == NULL)
    					m_pSchemaMap = new CMapPtrToPtr;
    				ASSERT_VALID(m_pSchemaMap);
    				m_pSchemaMap-> SetAt(pClassRef, (void*)nSchema);
    			}
    		}
    		CheckCount();
    		m_pLoadArray-> InsertAt(m_nMapCount++, pClassRef);
    	}
    	else
    	{
    		// existing class index in obTag followed by new object
    		DWORD nClassIndex = (obTag & ~dwBigClassTag);
    		if (nClassIndex == 0 || nClassIndex > (DWORD)m_pLoadArray-> GetUpperBound())
    			AfxThrowArchiveException(CArchiveException::badIndex,
    			m_strFileName);
    
    		pClassRef = (CRuntimeClass*)m_pLoadArray-> GetAt(nClassIndex);
    		ASSERT(pClassRef != NULL);
    
    		// determine schema stored against objects of this type
    		void* pTemp;
    		BOOL bFound = FALSE;
    		nSchema = 0;
    		if (m_pSchemaMap != NULL)
    		{
    			bFound = m_pSchemaMap-> Lookup( pClassRef, pTemp );
    			if (bFound)
    				nSchema = (UINT)pTemp;
    		}
    		if (!bFound)
    			nSchema = pClassRef-> m_wSchema & ~VERSIONABLE_SCHEMA;
    	}
    
    
    	// check for correct derivation
    	if (pClassRefRequested != NULL &&
    		!pClassRef-> IsDerivedFrom(pClassRefRequested))
    	{
    		AfxThrowArchiveException(CArchiveException::badClass, m_strFileName);
    	}
    
    	// store nSchema for later examination
    	if (pSchema != NULL)
    		*pSchema = nSchema;
    	else
    		m_nObjectSchema = nSchema;
    
    	// store obTag for later examination
    	if (pObTag != NULL)
    		*pObTag = obTag;
    
    	// return the resulting CRuntimeClass*
    	return pClassRef;
    }

    展开全文
  • CArchive详解

    千次阅读 2013-06-25 16:33:54
    ================================本文转自============================== ...   CArchive没有基类。 CArchive允许以一个永久二进制(通常为磁盘存储)的形式保存一个对象的复杂网络
  • CArchive原理

    2010-08-29 20:11:40
    FMD开发文集 -- CArchive原理 MFC 提供CArchive类实现数据的缓冲区读写,同时定义了类对象的存储与读取方案。 以下对CArchvie 的内部实现作分析。 一.概述 CArchive使用了缓冲区,即一段内存空间作为临时数据...
  • 认识CArchive

    千次阅读 2017-02-15 22:38:29
    序列化 当我们创建对象的时候,...当应用程序重新启动后,CArchive类可以帮助我们从磁盘文件读取这些数据,然后在内存中重新构建对应的对象;这样就使得我们的对象数据永久存在,该过程称之为序列化(或者串行化)
  • CSocket CSocketFile CArchive 流程
  • CArchive

    2012-03-08 12:05:19
    CArchive没有基类。 CArchive允许以一个永久二进制(通常为磁盘存储)的形式保存一个对象的复杂网络,它可以在对象被删除时,还能永久保存。可以从永久存储中装载对象,在内存中重新构造它们。使得数据永久保留的...
  • Windows——CArchive

    千次阅读 2020-06-11 16:44:46
    CArchive 对象提供了一个类型安全缓冲机制,用于将可序列化对象写入 CFile 对象或从中读取可序列化对象。通常,CFile 对象表示磁盘文件;但是,它也可以是表示“剪贴板”的内存文件(CSharedFile 对象)。 ...
  • MFC浅析 8 CArchive 原理

    2019-01-14 16:42:02
    MFC浅析 8 CArchive 原理
  • CArchive源码分析

    2013-11-14 12:32:39
    CArchive的功能: 1.CArchive封装了文件的读写操作,使文件读写变的更简单,其内部重载了> 使读写文件如使用cin cout一样 2.CArchive提供了缓存机制,使文件操作更高效 3.CArchive不仅只是封装文件操作,他还能够...
  • CArchive原理

    2010-03-26 23:37:00
    CArchive原理 自撰 2001年9月12日23:58 MFC 提供CArchive类实现数据的缓冲区读写,同时定义了类对象的存储与读取方案。 以下对CArchvie 的内部实现作分析。 1.概述 2.内部数据 3.基本数据读写 4.缓
  • 使用CArchive::IsLoading或CArchive::IsStoring函数,用于决定是否装载或存储了档案文件。 步骤是 1.必须在创建一个CArchive对象之前,创建一个CFile对象。 2.创建CArchive对象,要把它附加给表示一个打开文.
  • CArchive类读写

    2012-03-14 21:33:08
    CFile* pFileName=new CFile("myfile.txt", CFile::modeCreate| ... CArchive SaveArchive(pFileName, CArchive::store| CArchive::bNoFlushOnDelete);//当删除指针时不强制刷新缓冲 SaveAr
  • CFile和CArchive进行绑定,CArchive把数据写到文件中,代码如下: CFile file; BOOL isOk = file.Open(L"1.txt", CFile::modeCreate | CFile::modeWrite); if (!isOk) { return; } CArchive ar(&...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 804
精华内容 321
关键字:

carchive