文件的合并与分离(FILE*)

appleyk 2012-07-23 07:57:49
问题:如果你想把一个或多个文件流写入一个文件中,并能实现回放的功能,也就是把之前写入的文本数据按照原来的写入方式分离出来,你要怎么写,又怎么分离?

PS:可能好多人已经接触过这类题型,也或者有些参加工作的人遇到过这样类似的面试题,也或者一些才入门的C/C++童鞋们,你们也正在思考这个问题并被困扰着如何下手。

下面我就来说明下我的思路,并附上源码(代码没有封装,只供参考),请大大们指出不足之处,不要喷,如果你们有更好的解决办法,也可以拿出来一起探讨探讨!


Okay...如果你只把一个文本文件读出并写入另一个文件中,ok,什么都不用考虑,fwrite fread直接用就行
但是如果给你两个文本,你难道也那样嘛?如果你不考虑那个回放的功能也就是分离功能的话,你也大可不必
考虑什么,只需当第一个文本文件数据写入结束时,将当前的写入指针比如fpwrite指向末尾就行,fseek就能办到
-->fseek(fpwrite,0,SEEK_END);然后再写入第二个,这样就避免了两个文本写入时被覆盖的情况,初步达到友好
合并的效果,但是问题出现了,你怎么样把这两个文件的内容很完美的从当前的文件中
分离出来再分别保存到两个文件中呢?有些人说,这不好办,我每写入一次文件,在文件末尾做个标记MARK,那么我分离的时候只要每次匹配我的MARK就行了啊!?但是,你的MARK肯定是受限制的吧?万一你的MARK标记的内容正好是你写入的文件里面数据的一部分呢?如果这样,坏了,你分离的肯定是乱七八糟,断断续续,这不坑人嘛!

Then....也许你们还有其他的想法,现在我来说说我的想法。
先就一个文本文件来分析。它包括三个部分,文件名称f_name,文件内容buffer,以及文件存储的字节数f_count
对于文件内容,你可以很轻松的用fread读出来,至于,f_name和f_count,你可以定义一个结构体,来存储文件的
基本信息,包括文本的名字和文本的字节数;

如:
typedef struct _tg_struct
{
char f_name[30];
int savef_count;


}SAVEFILE;//总过30+2+4个字节

前30个字节存储文件名称,后4个字节存储文本内容长度

这样如果你写入一个文件,也就是写入一个 结构体+内容 的格式,如果你再写入一个文件,也是这样,这样的好处,体现在分离文件的时候,比如,你若要分离出第一个文件,只需要fread的时候,取出第一个文件头中的信息,分别匹配名字和按长度读取savef_count次,就可以读到第一个文件的末尾,blablablabla,然后控制下指针位置,
fseek(fpread,sizeof(SAVEFILE)+savef_count,SEEK_SET);这样的话,当前指针的位置就来到了第二个文件的文件头这里
然后,参考第一个文件的读取方式,再分离第二个无非就是copy一下代码(如果不想,封装起来把!)

OK,可能你们还有疑问,怎么样计算文件内容的长度,又怎么样根据savef_count读取到一个文件的结尾?

下面附上代码,有点乱。。。。


#include <stdio.h>
#include "string.h"
#define NULL 0
typedef struct _tg_struct
{
char f_name[30];
int savef_count;


}SAVEFILE;

enum
{
ERROR_PARAM,
ERROR_FILE,
SUCCESS,


};
int SaveFile(const char* fSrc1,const char* fSrc2,const char* fDes );//合并文件夹

int ReadFile(char* fDes,const char* fSrc1,const char* fSrc2const ); //分离文件夹

int main()
{
printf("正在写文件,请稍等.....");
SaveFile("c:\\a.txt","c:\\b.txt","c:\\c.txt");
printf("正在读文件,请稍等.....");
ReadFile("c:\\c.txt","c:\\d.txt","c:\\e.txt");

getchar();
return 0;

}

int SaveFile(const char* fSrc1,const char* fSrc2,const char* fDes )
{
SAVEFILE savef;
FILE* fpWrite,*fpRead;
char buffer[1024];
int a = 0;
int b = 0;

memset(&savef,0,sizeof(SAVEFILE));
fpWrite=fopen(fDes,"wb+");//写入目标文件
strcpy(savef.f_name,fSrc1);
fpRead=fopen(fSrc1,"rb");//读源文件1

if(fpWrite==NULL || fpRead==NULL)
{
return ERROR_PARAM;
}


fwrite(&savef,1,sizeof(SAVEFILE),fpWrite);//写文件头 此时savef.savef_count=0

while(!feof(fpRead))
{
a=0;
a=fread(buffer,1,1000,fpRead);//将内容读入到buffer中
printf("a=%d\n",a);

savef.savef_count+=a;//累计文件内容的字节数

fwrite(buffer,1,a,fpWrite);//读了再写入内容
}
fseek(fpWrite,0,SEEK_SET);//将指针置为文件开始处

fwrite(&savef,1,sizeof(SAVEFILE),fpWrite);//重写文件头 此时savef.savef_count!= 0

printf("savef_count=%d\n",savef.savef_count);

fseek(fpWrite,0,SEEK_END);//将文件指针指向文件最后

b=savef.savef_count;//这个留着下次写时有用

fclose(fpRead);


memset(&savef,0,sizeof(SAVEFILE));//如果对大文件进行置空的话,是非常耗费CPU的
strcpy(savef.f_name,fSrc2);//
printf("file2_name=%s\n",savef.f_name);
fpRead=fopen(fSrc2,"rb");//打开第二个文件

fwrite(&savef,1,sizeof(SAVEFILE),fpWrite);//写文件头信息

while(!feof(fpRead))
{
a=0;
a=fread(buffer,1,1000,fpRead);
printf("a=%d\n",a);
savef.savef_count+=a;
fwrite(buffer,1,a,fpWrite);//读了再写入内容
}

fseek(fpWrite,sizeof(SAVEFILE)+b,SEEK_SET);

fwrite(&savef,1,sizeof(SAVEFILE),fpWrite);

printf("savef_count=%d",savef.savef_count);

fclose(fpWrite);
fclose(fpRead);

}

int ReadFile(const char* fSrc,const char* fDes1,const char* fDes2 )
{
FILE *p_fwrite;//写
FILE *p_fread; //读
SAVEFILE savef;
char buffer[1024];

int a = 0;//字节数
int b = 0;//文件内容所占字节数
int current=0;//保存第一次分离的文件的内容所占的字节数

memset(&savef,0,sizeof(SAVEFILE));//置空结构体
p_fread=fopen(fSrc,"rb");//读写二进制
p_fwrite=fopen(fDes1,"wb+");//写附加

if(p_fwrite == NULL || p_fread == NULL)
{
printf("File open failed.....");
return ERROR_PARAM;
}

//将p_fread中指向的大小为sizeof(SAVEFILE)的内容读入到结构体savef中

a=fread(&savef,1,sizeof(SAVEFILE),p_fread);

//savef包括两个部分,一个30字节的文件头 一个4字节的文件内容字节数

b=savef.savef_count;
current=b;//记下

printf("\nThe first file bytes is %d\n",b);
if(b > 0)
{
//如果大于0 存在内容 循环控制读取
while(b >= 1000)
{
a=0;
a=fread(buffer,1,1000,p_fread);//读入buffer保存读取数据
b-=a;//如果b<1000 则跳出循环 然后按照一次b个字节读取
fwrite(buffer,1,a,p_fwrite);//第一次分离文件,写指针指向fDes1

if(b < 1000)
break;

}

if(b < 1000 && b > 0)
{

a=fread(buffer,1,b,p_fread);//一次读完
fwrite(buffer,1,a,p_fwrite);//一次写完
}

}//end first

fclose(p_fwrite);//关闭第一次读的文件指针

printf("分离并写入%s成功\n\n",savef.f_name);

//第二次读

memset(&savef,0,sizeof(SAVEFILE));//再一次置空,防止数据干扰
p_fwrite=fopen(fDes2,"wb+");//分离第二个文件

fseek(p_fread,sizeof(SAVEFILE)+current,SEEK_SET);//当前读指针指向第二个文件头处

a=fread(&savef,1,sizeof(SAVEFILE),p_fread);
printf("\nsave_count=%d\n",savef.savef_count);
b=savef.savef_count;//第二个文件内容所占的字节数

//同上面,流程一样 最好封装一下
printf("\nThe first file bytes is %d\n",b);
if(b > 0)
{
while(b <= 1000)
{
a=0;
a=fread(buffer,1,1000,p_fread);
b-=a;
fwrite(buffer,1,a,p_fwrite);

//判断下b的值

if(b < 1000)
break;//直接跳出
}

if(b > 0 && b < 1000)
{
a=fread(buffer,1,b,p_fread);
//再一次写入剩下不足1000字节的内容

fwrite(buffer,1,a,p_fwrite);

}

}//end second

printf("分离并写入%s成功",savef.f_name);

fclose(p_fwrite);//关闭
fclose(p_fread);




}



...全文
301 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
kagetu_toki 2012-07-24
  • 打赏
  • 举报
回复
c区现在这么冷清了么
感觉是不是可以单独存一个文件来保存合并信息
赵4老师 2012-07-24
  • 打赏
  • 举报
回复
先安装WinRAR软件到目录C:\Program Files\WinRAR中,然后:
system("c:\\progra~1\\winrar\\rar.exe a package.rar ...");
system("c:\\progra~1\\winrar\\rar.exe vb package.rar >package.txt");
//然后读文件package.txt的内容
system("c:\\progra~1\\winrar\\rar.exe x package.rar ...");
AnYidan 2012-07-24
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 的回复:]

lz是把索引信息直接写到目标文件了,同意2楼,是否可以单独建一个索引文件。
[/Quote]

++
ryfdizuo 2012-07-24
  • 打赏
  • 举报
回复
lz是把索引信息直接写到目标文件了,同意2楼,是否可以单独建一个索引文件。
zhanshen2891 2012-07-24
  • 打赏
  • 举报
回复
文件系统?

参考一下暴雪的MPQ
wqkjj 2012-07-24
  • 打赏
  • 举报
回复
思路大概正确。
如果要考虑其他因素,比如使合并文件尽量小,比如可以使多进程互斥读写等等,则还有很多优化的空间。
赵4老师 2012-07-24
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 的回复:]
引用 8 楼 的回复:
先安装WinRAR软件到目录C:\Program Files\WinRAR中,然后:
system("c:\\progra~1\\winrar\\rar.exe a package.rar ...");
system("c:\\progra~1\\winrar\\rar.exe vb package.rar >package.txt");
//然后读文件packag……
[/Quote]
将里面的...替换为你要处理的文件名
appleyk 2012-07-24
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 的回复:]
先安装WinRAR软件到目录C:\Program Files\WinRAR中,然后:
system("c:\\progra~1\\winrar\\rar.exe a package.rar ...");
system("c:\\progra~1\\winrar\\rar.exe vb package.rar >package.txt");
//然后读文件package.txt的内容
sys……
[/Quote]

what is this.....额,不是很明白。
appleyk 2012-07-24
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 的回复:]
文件系统?

参考一下暴雪的MPQ
[/Quote]

看来我要慢慢看了,慢慢啃了,楼上的楼上+1.
appleyk 2012-07-24
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 的回复:]
引用 5 楼 的回复:

lz是把索引信息直接写到目标文件了,同意2楼,是否可以单独建一个索引文件。


++
[/Quote]

嗯嗯,不过如果这个索引文件单独建的话还是要写入读出的,万一数据遭到破坏,那岂不是得不偿失,写在目标文件,到时候可以加密的嘛,一举两得。
appleyk 2012-07-23
  • 打赏
  • 举报
回复
自己顶个

69,369

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧