??什么是打包?打包这个词很形象,就是把零碎的文件进行统一重封装,统一管理,比如我们常见的RAR文件,ZIP文件都是很常见的包裹格式
package.h
#pragma once
#include <vector>
//先从最简单最简单的开始,以ASCII编码为准
struct PackageItem
{
char FileName[256]; //假设文件名定长,不超过256字符
int FileSize; //假设文件不超过2G
unsigned int OffsetPackage; //在包裹的偏移,同样最简单的演示我们假设包裹文件不大于4G
};
class CPackage
{
public:
CPackage(void);
public:
~CPackage(void);
bool CreatePackage( const char*szPackageName ); //创建一个空白包裹
bool AddFileToPackage( const char*szFileName ); //添加一个文件到包裹
private:
FILE *m_fpPackage; //包裹文件指针
std::vector< PackageItem > m_PackageItems; //包裹文件信息,我们用vector来保存
};
Package.cpp
#include "StdAfx.h"
#include "Package.h"
CPackage::CPackage(void)
{
m_fpPackage = NULL;
}
CPackage::~CPackage(void)
{
if( m_fpPackage )
{
fclose( m_fpPackage );
m_fpPackage = NULL;
}
}
bool CPackage::CreatePackage(const char*szPackageName )
{
//最简单的例子,如果已经创建过包裹了,直接返回失败
if( m_fpPackage != NULL )
{
return false;
}
//我们先用标准的C方式创建文件
m_fpPackage = fopen( szPackageName, "wb" );
//打开文件失败,返回false
if( m_fpPackage == NULL )
{
printf( "打开文件%s失败\n", szPackageName );
return false;
}
return true;
}
bool CPackage::AddFileToPackage( const char*szFileName )
{
//演示方便,我们不做任何重复性重名判断
//打开目标文件
FILE*fp = fopen( szFileName, "rb" );
if( !fp )
{
printf( "打开文件%s失败\n", szFileName );
return false;
}
//我们先得到下这个文件大小
fseek( fp, 0, SEEK_END );
int FileSize = ftell( fp );
fseek( fp, 0, SEEK_SET );
//构建包裹数据
PackageItem Item;
strncpy( Item.FileName, szFileName, sizeof( Item.FileName ) - 1 );
Item.FileSize = FileSize;
Item.OffsetPackage = ftell( m_fpPackage );
//同样为了演示方便,我们不做任何写入判断
//先把包裹文件信息写入包裹
fwrite( &Item, 1, sizeof( Item ), m_fpPackage );
//写入文件数据,我们准备一个64K的缓冲区
char szBuffer[65536];
while( 1 )
{
//读取文件
int nReadBytes = fread( szBuffer, 1, sizeof( szBuffer ), fp );
//写入包裹
fwrite( szBuffer, 1, nReadBytes, m_fpPackage );
//如果读取到的数据比缓冲区小,那么说明读取结束了
if( nReadBytes < sizeof( szBuffer ) )
{
break;
}
}
//fflush是确保我们的数据写入到磁盘上了
fflush( m_fpPackage );
//关闭文件
fclose( fp );
return true;
}
PackageLoader.h
#pragma once
#include <vector>
//先从最简单最简单的开始,以ASCII编码为准
struct PackageItem
{
char FileName[256]; //假设文件名定长,不超过256字符
int FileSize; //假设文件不超过2G
unsigned int OffsetPackage; //在包裹的偏移,同样最简单的演示我们假设包裹文件不大于4G
};
class CPackageLoader
{
public:
CPackageLoader(void);
public:
~CPackageLoader(void);
//打开包裹szPackageName
bool OpenPackage( const char*szPackageName );
//得到包裹里面有多少个PackageItem(打包文件)
int GetPackageItemCount() const;
//得到打包文件的信息
const PackageItem* GetPackageItem( int Index );
//导出包裹文件并保存到szTargetName
bool ExportPackageItem( const PackageItem*pItem, const char*szTargetName );
private:
FILE *m_fpPackage;
std::vector< PackageItem > m_PackageItems;
};
PackageLoader.cpp
#include "StdAfx.h"
#include "PackageLoader.h"
#include <assert.h>
CPackageLoader::CPackageLoader(void)
{
m_fpPackage = NULL;
}
CPackageLoader::~CPackageLoader(void)
{
if( m_fpPackage )
{
fclose( m_fpPackage );
m_fpPackage = NULL;
}
}
bool CPackageLoader::OpenPackage(const char*szPackageName )
{
//为了演示,我们仅仅提供OpenPackage并不提供ClosePackage功能
if( m_fpPackage != NULL )
{
return false;
}
m_fpPackage = fopen( szPackageName, "rb" );
if( m_fpPackage == NULL )
{
printf( "打开%s失败\n", szPackageName );
return false;
}
//我们先读取所有的PackageItem信息
while( 1 )
{
//简单的格式,没有太多检查,复杂的我们稍后说
//读取PackageItem结构
PackageItem Item;
if( sizeof( Item ) != fread( &Item, 1, sizeof( Item ), m_fpPackage ) )
{
break;
}
m_PackageItems.push_back( Item );
//跳过压缩文件的大小,这样下一次循环中,我们就得到下一个Item了
fseek( m_fpPackage, Item.FileSize, SEEK_CUR );
}
return true;
}
int CPackageLoader::GetPackageItemCount() const
{
return m_PackageItems.size();
}
const PackageItem* CPackageLoader::GetPackageItem(int Index )
{
if( Index < 0 || Index >= m_PackageItems.size() )
{
return NULL;
}
return &m_PackageItems[Index];
}
bool CPackageLoader::ExportPackageItem( const PackageItem*pItem, const char*szTargetName )
{
//为了演示,我们这里去掉了m_fpPackage合法性判断, pItem合法性判断和szTargetName的判断
FILE*fp = fopen( szTargetName, "wb" );
if( !fp )
{
printf( "打开%s失败\n", szTargetName );
return false;
}
//移动文件指针到指定位置,由于我们的OffsetPackage数据包含了PackageItem信息
//所以打包文件的真实偏移还需要加上sizeof( PackageItem )
fseek( m_fpPackage, pItem->OffsetPackage + sizeof( PackageItem ), SEEK_SET );
//写入文件数据,我们准备一个64K的缓冲区
char szBuffer[65536];
//我们需要读取的文件大小
int LeftSize = pItem->FileSize;
while( 1 )
{
//实际要读取的大小
int ReadSize = LeftSize;
//如果大于缓冲区,我们就只读取缓冲区大小的内容,剩余的下次读取
if( ReadSize > sizeof( szBuffer ) )
{
ReadSize = sizeof( szBuffer );
}
//读取文件
int nReadBytes = fread( szBuffer, 1, ReadSize, m_fpPackage );
//ReadSize必须要等于nReadBytes,演示代码我们不做过多判断
assert( ReadSize == nReadBytes );
//写入包裹
fwrite( szBuffer, 1, nReadBytes, fp );
LeftSize -= nReadBytes;
//如果剩余大小为0,就读取完成了
if( LeftSize == 0 )
{
break;
}
}
fclose( fp );
return true;
}
原文:https://www.cnblogs.com/s3320/p/11923268.html