C++_MFC_CArchive类_第1页
C++_MFC_CArchive类_第2页
C++_MFC_CArchive类_第3页
C++_MFC_CArchive类_第4页
C++_MFC_CArchive类_第5页
已阅读5页,还剩17页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、FMD开发文集 - CArchive原理作者:冯明德MFC 提供CArchive类实现数据的缓冲区读写,同时定义了类对象的存储与读取方案。 以下对CArchvie 的内部实现作分析。 1.概述 2.内部数据 3.基本数据读写 4.缓冲区的更新 5.指定长度数据段落的读写 6.字符串的读写 7.CObject派生对象的读写 一.概述 CArchive使用了缓冲区,即一段内存空间作为临时数据存储地,对CArchive的读写都先依次排列到此缓冲区,当缓冲区满或用户要求时,将此段整理后的数据读写到指定的存储煤质。 当建立CArchive对象时,应指定其模式是用于缓冲区读,还是用于缓冲区写。 可以这样理

2、解,CArchive对象相当于铁路的货运练调度站,零散的货物被收集,当总量到达火车运量的时候,由火车装运走。 当接到火车的货物时,则货物由被分散到各自的货主。与货运不同的是,交货、取货是按时间循序执行的,而不是凭票据。因此必须保证送货的和取货的货主按同样的循序去存或取。 对于大型的货物,则是拆散成火车单位,运走,取货时,依次取各部分,组装成原物。 二.内部数据 缓冲区指针 BYTE* m_lpBufStart,指向缓冲区,这个缓冲区有可能是底层CFile(如派生类CMemFile)对象提供的,但一般是CArchive自己建立的。 缓冲区尾部指针 BYTE* m_lpBufMax; 缓冲区当前位

3、置指针 BYTE* m_lpBufCur; 初始化时,如果是读模式,当前位置在尾部,如果是写模式,当前位置在头部: m_lpBufCur = (IsLoading() ? m_lpBufMax : m_lpBufStart;三.基本数据读写 对于基本的数据类型,例如字节、双字等,可以直接使用">>"、"<<"符号进行读出、写入。 /操作符定义捕:/插入操作CArchive& operator<<(BYTE by);CArchive& operator<<(WORD w);CArchive&a

4、mp; 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<<(

5、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&

6、 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

7、(); /缓冲区内容提交到实际存储煤质。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) /缓冲区要读完了FillBuf

8、fer(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:Flus

9、h()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, IsS

10、toring();if (IsLoading()/ unget the characters in the buffer, seek back unused amountif (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_lpB

11、ufStart, m_lpBufCur - m_lpBufStart);else/如果是直接针对内存区域的的(例如CMemFile中) (只需移动相关指针,指向新的一块内存)if (m_lpBufCur != m_lpBufStart)m_pFile-> GetBufferPtr(CFile:bufferCommit, m_lpBufCur - m_lpBufStart);/ get next bufferVERIFY(m_pFile-> GetBufferPtr(CFile:bufferWrite, m_nBufSize,(void*)&m_lpBufStart, (vo

12、id*)&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_lpBufS

13、tart != 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_lpBu

14、fCur, 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

15、(m_lpBufStart, m_lpBufCur, nUnused);m_lpBufCur = m_lpBufStart;m_lpBufMax = m_lpBufStart + nUnused;/ read to satisfy nBytesNeeded or nLeft if possibleUINT nRead = nUnused;UINT nLeft = m_nBufSize-nUnused;UINT nBytes;BYTE* lpTemp = m_lpBufStart + nUnused;donBytes = m_pFile-> Read(lpTemp, nLeft);lpTe

16、mp = 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)

17、;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)AfxThrowArchiv

18、eException(CArchiveException:endOfFile);五.指定长度数据段落的读写以下分析 UINT Read(void* lpBuf, UINT nMax); 读取长度为nMax的数据 void Write(const void* lpBuf, UINT nMax); 写入指定长度nMax的数据 对于大段数据的读写,先使用当前缓冲区中的内容或空间读取或写入,若这些空间够用了,则结束。 否则,从剩余的数据中找出最大的缓冲区整数倍大小的一块数据,直接读写到存储煤质(不反复使用缓冲区)。 剩余的余数部分,再使用缓冲区读写。 (说明:缓冲区读写的主要目的是将零散的数据以缓冲区

19、大小为尺度来处理。对于大型数据,其中间的部分,不是零散的数据,使用缓冲区已经没有意思,故直接读写) 读取 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

20、_lpBufCur); memcpy(lpBuf, m_lpBufCur, nTemp);m_lpBufCur += nTemp;lpBuf = (BYTE*)lpBuf + nTemp; /移动读出内容所在区域的指针nMaxTemp -= nTemp;/当前缓冲区中剩余部分不够要求读入的长度。/还有字节需要读,则需要根据需要执行若干次填充缓冲区,读出,直到读出指定字节。if (nMaxTemp != 0) /计算出去除尾数部分的字节大小(整数个缓冲区大小) /对于这些部分,字节从文件对象中读出,放到输出缓冲区nTemp = nMaxTemp - (nMaxTemp % m_nBufSize)

21、; UINT nRead = 0;UINT nLeft = nTemp;UINT nBytes;donBytes = m_pFile-> Read(lpBuf, nLeft); /要求读入此整数缓冲区部分大小lpBuf = (BYTE*)lpBuf + nBytes;nRead += nBytes;nLeft -= nBytes;while (nBytes > 0) && (nLeft > 0); 知道读入了预定大小,或到达文件尾nMaxTemp -= nRead;if (nRead = nTemp) /读入的字节等于读入的整数倍部分 该读最后的余数部分了/

22、建立装有此最后余数部分的内容的CArchive的工作缓冲区。if (!m_bDirectBuffer)UINT nLeft = max(nMaxTemp, (UINT)m_nBufSize);UINT nBytes;BYTE* lpTemp = m_lpBufStart;nRead = 0;donBytes = m_pFile-> Read(lpTemp, nLeft); /从文件中读入到CArchive缓冲区lpTemp = lpTemp + nBytes;nRead += nBytes;nLeft -= nBytes;while (nBytes > 0) &&

23、(nLeft > 0) && nRead < nMaxTemp);m_lpBufCur = m_lpBufStart;m_lpBufMax = m_lpBufStart + nRead;elsenRead = m_pFile-> GetBufferPtr(CFile:bufferRead, m_nBufSize,(void*)&m_lpBufStart, (void*)&m_lpBufMax);ASSERT(nRead = (UINT)(m_lpBufMax - m_lpBufStart);m_lpBufCur = m_lpBufStart;/

24、读出此剩余部分到输出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_lpB

25、ufCur);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

26、 (m_bDirectBuffer)/ sync up direct mode buffer to new file positionVERIFY(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 t

27、o active bufferASSERT(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); /调用Wri

28、te,将字符串对应的一段数据写入字符串读(读取一行字符串) 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

29、;ASSERT(AfxIsValidAddress(lpsz, (nStop+1) * sizeof(TCHAR);_TUCHAR ch;int nRead = 0;TRYwhile (nRead < nStop)*this >> ch; /读出一个字节/ stop and end-of-line (trailing ''n'' is ignored) 遇换行回车if (ch = ''n'' | ch = ''r'')if (ch = ''r'')*

30、this >> ch;/ store the newline when called with negative nMaxif (int)nMax != nStop)lpsznRead+ = ch;break;lpsznRead+ = ch;CATCH(CArchiveException, e)if (e-> m_cause = CArchiveException:endOfFile)DELETE_EXCEPTION(e);if (nRead = 0)return NULL;elseTHROW_LAST();END_CATCHlpsznRead = ''0&#

31、39;'return lpsz;ReadString到CString对象,可以多行字符 BOOL CArchive:ReadString(CString& rString)rString = &afxChNil; / empty string without deallocatingconst int nMaxSize = 128;LPTSTR lpsz = rString.GetBuffer(nMaxSize);LPTSTR lpszResult;int nLen;for (;)lpszResult = ReadString(lpsz, (UINT)-nMaxSize

32、); / store the newlinerString.ReleaseBuffer();/ if string is read completely or EOFif (lpszResult = NULL |(nLen = lstrlen(lpsz) < nMaxSize |lpsznLen-1 = ''n'')break;nLen = rString.GetLength();lpsz = rString.GetBuffer(nMaxSize + nLen) + nLen;/ remove ''n'' from end

33、of string if presentlpsz = rString.GetBuffer(0);nLen = rString.GetLength();if (nLen != 0 && lpsznLen-1 = ''n'')rString.GetBufferSetLength(nLen-1);return lpszResult != NULL;使用CString对象的"<<"与">>"符读写字符串 CString定义了输入输出符,可以象基本类型的数据一样使用CArchive 的操作符定

34、义 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:B

35、YTE, TCHAR chars/ if >= 0xff characters: 0xff, len:WORD, TCHAR chars/ if >= 0xfffe characters: 0xff, 0xffff, len:DWORD, TCHARsCArchive& AFXAPI operator<<(CArchive& ar, const CString& string)/ special signature to recognize unicode strings#ifdef _UNICODEar << (BYTE)0xff;

36、ar << (WORD)0xfffe;#endifif (string.GetData()-> nDataLength < 255)ar << (BYTE)string.GetData()-> nDataLength;else if (string.GetData()-> nDataLength < 0xfffe)ar << (BYTE)0xff;ar << (WORD)string.GetData()-> nDataLength;elsear << (BYTE)0xff;ar << (

37、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 archiveAFX_STATIC UINT AFXAPI _AfxReadStringLength(CArchive& ar)DWORD nNewLen;/ att

38、empt BYTE length firstBYTE bLen;ar >> bLen;if (bLen < 0xff)return bLen;/ attempt WORD lengthWORD wLen;ar >> wLen;if (wLen = 0xfffe)/ UNICODE string prefix (length will follow)return (UINT)-1;else if (wLen = 0xffff)/ read DWORD of lengthar >> nNewLen;return (UINT)nNewLen;elseretu

39、rn wLen;CArchive& AFXAPI operator>>(CArchive& ar, CString& string)#ifdef _UNICODEint nConvert = 1; / if we get ANSI, convert#elseint nConvert = 0; / if we get UNICODE, convert#endifUINT nNewLen = _AfxReadStringLength(ar);if (nNewLen = (UINT)-1)nConvert = 1 - nConvert;nNewLen = _Afx

40、ReadStringLength(ar);ASSERT(nNewLen != -1);/ set length of string to new lengthUINT nByteLen = nNewLen;#ifdef _UNICODEstring.GetBufferSetLength(int)nNewLen);nByteLen += nByteLen * (1 - nConvert); / bytes to read#elsenByteLen += nByteLen * nConvert; / bytes to readif (nNewLen = 0)string.GetBufferSetL

41、ength(0);elsestring.GetBufferSetLength(int)nByteLen+nConvert);#endif/ read in the charactersif (nNewLen != 0)ASSERT(nByteLen != 0);/ read new dataif (ar.Read(string.m_pchData, nByteLen) != nByteLen)AfxThrowArchiveException(CArchiveException:endOfFile);/ convert the data if as necessaryif (nConvert !

42、= 0)#ifdef _UNICODECStringData* pOldData = string.GetData();LPSTR lpsz = (LPSTR)string.m_pchData;#elseCStringData* pOldData = string.GetData();LPWSTR lpsz = (LPWSTR)string.m_pchData;#endiflpsznNewLen = ''0'' / must be NUL terminatedstring.Init(); / don''t delete the old datas

43、tring = lpsz; / convert with operator=(LPWCSTR)CString:FreeData(pOldData);return ar;七.CObject派生对象的读写 MFC中多数类都从CObject类派生,CObject类与CArchive类有着良好的合作关系,能实现将对象序列化储存到文件或其他媒介中去,或者读取预先储存的对象,动态建立对象等功能。 CObject定义了针对CArvhive的输入输出操作符,可以向其他基本数据类型一样使用"<<"、"<<"符号 CArchive& AFXA

44、PI 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中

45、先写入或读取运行时类信息(CRuntimeClas),再调用Serialze(.),按其中的代码读写具体的对象数据。 因此,只要在CObject派生类中重载Serilize()函数,写入具体的读写过程,就可以使对象具有存储与创建能力。/将对象写入到缓冲区void CArchive:WriteObject(const CObject* pOb)DWORD nObIndex;/ make sure m_pStoreMap is initializedMapObject(NULL);if (pOb = NULL)/ save out null tag to represent NULL pointe

46、r*this << wNullTag;else if (nObIndex = (DWORD)(*m_pStoreMap)(void*)pOb) != 0)/ assumes initialized to 0 map/ save out index of already stored objectif (nObIndex < wBigObjectTag)*this << (WORD)nObIndex;else*this << wBigObjectTag;*this << nObIndex;else/ write class of object

47、 firstCRuntimeClass* pClassRef = pOb-> GetRuntimeClass();WriteClass(pClassRef); /写入运行类信息/ enter in stored object table, checking for overflowCheckCount();(*m_pStoreMap)(void*)pOb = (void*)m_nMapCount+;/ 调用CObject的Serialize成员,按其中的代码写入类中数据。(CObject*)pOb)-> Serialize(*this);CObject* CArchive:Read

48、Object(const CRuntimeClass* pClassRefRequested)/ attempt to load next stream as CRuntimeClassUINT nSchema;DWORD obTag;/先读入运行时类信息CRuntimeClass* pClassRef = ReadClass(pClassRefRequested, &nSchema, &obTag);/ check to see if tag to already loaded objectCObject* pOb;if (pClassRef = NULL)if (obTag

49、 > (DWORD)m_pLoadArray-> GetUpperBound()/ tag is too large for the number of objects read so farAfxThrowArchiveException(CArchiveException:badIndex,m_strFileName);pOb = (CObject*)m_pLoadArray-> GetAt(obTag);if (pOb != NULL && pClassRefRequested != NULL && !pOb-> IsKindOf(

50、pClassRefRequested)/ loaded an object but of the wrong classAfxThrowArchiveException(CArchiveException:badClass,m_strFileName);else/ 建立对象pOb = pClassRef-> CreateObject();if (pOb = NULL)AfxThrowMemoryException();/ Add to mapping array BEFORE de-serializingCheckCount();m_pLoadArray-> InsertAt(m_

51、nMapCount+, pOb);/ Serialize the object with the schema number set in the archiveUINT nSchemaSave = m_nObjectSchema;m_nObjectSchema = nSchema;pOb-> Serialize(*this); /调用CObject的Serialize,按其中代码读入对象数据。m_nObjectSchema = nSchemaSave;ASSERT_VALID(pOb);return pOb;运行时类信息的读写 为了避免众多重复的同类对象写入重复的类信息,CArchiv

52、e中使用CMap对象储存和检索类信息。void CArchive:WriteClass(const CRuntimeClass* pClassRef)ASSERT(pClassRef != NULL);ASSERT(IsStoring(); / proper directionif (pClassRef-> m_wSchema = 0xFFFF)TRACE1("Warning: Cannot call WriteClass/WriteObject for %hs.n",pClassRef-> m_lpszClassName);AfxThrowNotSupport

53、edException();/ make sure m_pStoreMap is initializedMapObject(NULL);/ write out class id of pOb, with high bit set to indicate/ new object follows/ ASSUME: initialized to 0 mapDWORD nClassIndex;if (nClassIndex = (DWORD)(*m_pStoreMap)(void*)pClassRef) != 0)/ previously seen class, write out the index

54、 tagged by high bitif (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 overflowCheckCount();(*m_pStoreMap)(void*)pClassRef = (void*)m_nMapCount+;CRuntimeClass* CArchive:ReadClass(const CRuntim

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论