`
sogotobj
  • 浏览: 675641 次
  • 性别: Icon_minigender_2
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

C语言版的磁盘文件分片归并排序函数

阅读更多

这是一个很老的的C函数,用来实现大的磁盘文件排序。在以前DOS操作系统下,对磁盘文件的排序一般有3种方法:1、将磁盘文件装入内存排序,将排序结果保存到新的文件,这适用于很小的(64K以内)、不需要经常索引的文件;2、对磁盘文件按关键字进行分块排序后,形成一个索引文件。块的大小一般为512K,常采用B+树或者B-数算法,这种方法适用于需要经常索引的磁盘文件,如DBF文件;3、把磁盘文件分片排序后,形成很多排序片文件,然后将这些排序片文件合并起来,输出为一个排序文件,这种方法适用于很大的、但又不需要经常索引的磁盘文件。

可见,在DOS有限的内存条件下,磁盘文件分片归并排序是使用比较广泛的一种外存储器排序算法。现在计算机的物理内存一般足够大(最小的也有256MB吧),Windows的虚拟内存更是多达4个GB(对每一个应用程序而言),这对于很多磁盘文件的内存排序应该是足够了,况且现在的记录文件都放在各种数据库中,所以磁盘文件分片归并排序算法可能没有市场了(不过内存多路归并排序还是有市场的)。作为怀旧,把代码贴在这里,以免“失传”!


/**************************************************************************
*文件名:MERGE.H*
*编制人:湖北省公安县统计局毛泽发*
*日期:1991.8*
*************************************************************************
*/

#defineS_IREAD0x0100
#defineS_IWRITE0x0080

#ifdefined(__TINY__)||defined(__SMALL__)||defined(__MENIUM__)
#defineSSIZE25600/*排序缓冲区字节*/
#defineNULL0
#else
#defineSSIZE65024/*排序缓冲区字节*/
#defineNULL0L
#endif
#defineMAXMERGE4/*排序合并每趟每次最大片*/
#defineMAXMEREC(SSIZE/(MAXMERGE+1))/*文件最大记录长*/

typedef
intcdeclmercmpf(constvoid*,constvoid*);

/*通用排序函数.
参数:排序文件名;原文件名;原文件头字节数;文件记录长;用户提供的比较函数.
返回值:成功>0;内存不够.记录超长返回0;文件操作出错-1
*/
intfmerge(char*foname,char*finame,intftops,intlrd,mercmpf*cmpf);

/**************************************************************************
*文件名:MERGE.C*
*编制人:湖北省公安县统计局毛泽发*
*日期:1991.8*
*************************************************************************
*/

#include
<io.h>
#include
<string.h>
#include
<fcntl.h>
#include
<stdio.h>
#include
<stdlib.h>
#include
"merge.h"

staticmercmpf*mercmp = NULL;/*比较函数*/

staticchar*merbuf=NULL;/*排序动态缓冲区*/
staticchar*filetop=NULL;/*原文件文件头存放动态缓冲区*/
staticintfiletopchs;/*原文件文件头长*/
staticintmerlrd;/*文件记录长*/

staticintoutfile(char*fname,unsignedsize,intflag);
staticintformerge(char*foname,char*finame,char*tmp,unsignedm);
staticdomerge(char*foname,char*tmp1,char*tmp2,intirun);
staticvoidsmerge(int*md,intm,char*buf[],intoutf,char*outbuf,intsize);
staticintdopass(char*name1,char*name2,intirun);

/*通用排序函数.
参数:排序文件名;原文件名;原文件头字节数;文件记录长;用户提供的比较函数.
返回值:成功>0;内存不够.记录超长返回0;文件操作出错-1
*/
intfmerge(char*foname,char*finame,intftops,intlrd,mercmpf*cmpf)
{
chartmp1[68],tmp2[68];
intirun;
unsignedsize;
if(lrd>MAXMEREC)return0;/*记录超长*/
merlrd
=lrd;
size
=(SSIZE/lrd)*lrd;/*排序缓冲区实际长*/
if((merbuf=(char*)malloc(size))==NULL)return0;/*分配动态缓冲区*/
if(ftops&&(filetop=(char*)malloc(ftops))==NULL)return0;
filetopchs
=ftops;
mercmp
=cmpf;
strcpy(tmp1,
"&&&1");/*临时文件名*/
strcpy(tmp2,
"&&&2");
irun
=formerge(foname,finame,tmp1,size);/*分片排序*/
if(irun>1)/*如果排序片大于1*/
irun
=domerge(foname,tmp1,tmp2,irun);/*合并排序片*/
free(merbuf);
if(filetopchs)free(filetop);
returnirun;
}
/*写一排序片文件*/
staticintoutfile(char*fname,unsignedsize,intflag)
{
inth,c;
if((h=open(fname,O_WRONLY|O_CREAT|O_TRUNC|O_BINARY,S_IWRITE))==-1)
return-1;
if(flag&&filetopchs)/*如果是最终文件同时原文件有文件头*/
write(h,filetop,filetopchs);
/*写入文件头内容*/
c
=write(h,merbuf,size);/*写排序片到文件*/
close(h);
returnc;
}
/*分片排序*/
staticintformerge(char*foname,char*finame,char*tmp,unsignedm)
{
unsignedirun,ret;
intf,flag=0;
chartmpname[68];
if((f=open(finame,O_RDONLY|O_BINARY))==-1)return-1;/*打开原文件*/
if(filetopchs)/*如有文件头,保存其内容到缓冲区*/
read(f,filetop,filetopchs);
irun
=0;
do{
ret
=read(f,merbuf,m);/*读一排序片到排序缓冲区*/
if(ret==0||ret==0xffff)break;/*原文件结束或出错,退出*/
qsort(merbuf,ret
/merlrd,merlrd,mercmp);/*排序*/
if(ret==m||irun>0)/*如原文件长大于或等于一排序片长*/
sprintf(tmpname,
"%s.%03d",tmp,irun);/*采用临时文件名*/
else{/*否则,直接用排序文件名*/
strcpy(tmpname,foname);
flag
=1;/*最终文件标记*/
}
ret
=outfile(tmpname,ret,flag);/*写排序片*/
irun
++;
}
while(ret==m);
close(f);
if(ret==0xffff)returnret;/*出错返回-1*/
returnirun;/*返回排序片数*/
}
/*分配每一合并趟不同临时文件名;控制合并趟数*/
staticdomerge(char*foname,char*tmp1,char*tmp2,intirun)
{
char*p;
while(irun>1){
if(irun<=MAXMERGE)strcpy(tmp2,foname);
irun
=dopass(tmp1,tmp2,irun);
p
=tmp1;
tmp1
=tmp2;
tmp2
=p;
}
returnirun;
}
/*执行合并趟,计算.分配每次合并所需文件数,缓冲区大小,控制每次合并的执行*/
staticintdopass(char*name1,char*name2,intirun)
{
intfi,i,nrun,m,size;
charoname[68],inname[68],*p[MAXMERGE],*q;
intmd[MAXMERGE],fo;
size
=SSIZE/merlrd;/*合并缓冲区容纳记录数*/
nrun
=0;
for(fi=0;fi<irun;fi+=MAXMERGE){
m
=irun-fi;/*每次合并实际排序片数*/
if(m>MAXMERGE)m=MAXMERGE;
for(i=0;i<m;i++)p[i]=merbuf+(i*merlrd);/*分配读缓冲区*/
if(irun<=MAXMERGE)strcpy(oname,name2);/*最终合并形成排序文件*/
elsesprintf(oname,"%s.%03d",name2,nrun);/*中间合并采用临时文件*/
if((fo=open(oname,O_WRONLY|O_CREAT|O_TRUNC|O_BINARY,S_IWRITE))==-1)
break;/*打开写文件*/
i
=0;
do{/*分别打开读文件*/
sprintf(inname,
"%s.%03d",name1,fi+i);
md[i]
=open(inname,O_RDONLY|O_BINARY);
}
while(md[i++]!=-1&&i<m);
if(i!=m){
close(fo);
for(fi=0;fi<i;fi++)close(md[fi]);
break;
}
if(irun<=MAXMERGE&&filetopchs)/*最终合并写文件头(如有)*/
write(fo,filetop,filetopchs);
q
=merbuf+(m*merlrd);/*分配写缓冲区*/
smerge(md,m,p,fo,q,size
-m);/*合并*/
for(i=0;i<m;i++){/*删除各排序片文件*/
close(md[i]);
sprintf(inname,
"%s.%03d",name1,fi+i);
unlink(inname);
}
close(fo);
nrun
++;
}
if(nrun!=(irun+MAXMERGE-1)/MAXMERGE)return-1;
returnnrun;
}
/*执行实际排序片合并*/
staticvoidsmerge(int*md,intm,char*buf[],intoutf,char*outbuf,intsize)
{
inti,j,n=merlrd,w=merlrd*size;
char*s=buf[0],*p,*q=outbuf,*end=q+w;
for(i=0;i<m;i++)/*从各片文件中读第一条记录*/
read(md[i],buf[i],n);
while(1){
if(n==merlrd){/*如各片文件均有记录,各片记录反向插入排序*/
for(i=1;i<m;i++){
for(p=buf[i],j=i-1;j>=0&&mercmp(p,buf[j])>0;j--)
buf[j
+1]=buf[j];
buf[j
+1]=p;
}
}
elsem--;/*一片文件内容结束*/
if(!m){/*如所有片文件结束,写缓冲区残余记录,退出*/
if(q!=outbuf)write(outf,outbuf,q-outbuf);
break;
}
if(q==end){/*刷新一次写缓冲区到文件*/
if(write(outf,outbuf,end-outbuf)!=w)break;
q
=outbuf;
}
i
=m-1;
j
=(buf[i]-s)/merlrd;
memmove(q,buf[i],merlrd);
/*将各片记录中值最小(大)者移入写缓冲区*/
q
+=merlrd;
n
=read(md[j],buf[i],merlrd);/*从该片中读下一记录,继续*/
}
}

可以看到,上面2个文件时间是1991年的,真是老古董了,如MERGE.H文件开头就没有什么诸如#ifndef __MERGE_H......的代码,我记得那个时候好像没这个写法的。函数里面当初也作了很详细的注释,所以算法就不再讲了(要讲我还得先分析代码,早忘记了 ^_^ )。

为了示范该函数的使用方法,我还是用BCB6写了一个简单的演示程序,如果你想试一下老古董,不妨也写一个?可以将MERGE.H文件中的排序缓冲区加大一些,可提高排序速度。

//---------------------------------------------------------------------------
#include<stdio.h>
#include
<stdlib.h>
#include
"merge.h"

#pragmahdrstop

#defineTOPSTRING"湖北省公安县统计局毛泽发"
#defineTOP_SIZE30
#defineRECORD_SIZE53
#defineRECORD_COUNT10000

//---------------------------------------------------------------------------
/*
为了方便观察,随机生成了一个RECORD_COUNT行的文本文件*/
voidMakeFile(char*filename)
{
inti,j;
longv[4];
FILE
*f;
f
=fopen(filename,"w");
fprintf(f,
"%s ",TOPSTRING);
randomize();
for(i=0;i<RECORD_COUNT;i++)
{
for(j=0;j<4;j++)
v[j]
=random(0x7fffffff);
fprintf(f,
"%12ld%12ld%12ld%12ld ",v[0],v[1],v[2],v[3]);
}
fclose(f);
}


intcdeclCompRecord(constvoid*ra,constvoid*rb)
{
inta[4],b[4];
inti,n;
sscanf((
char*)ra,"%ld%ld%ld%ld",&a[0],&a[1],&a[2],&a[3]);
sscanf((
char*)rb,"%ld%ld%ld%ld",&b[0],&b[1],&b[2],&b[3]);
for(n=0,i=0;i<4&&n==0;i++)
n
=a[i]-b[i];
returnn;
}

#pragmaargsused
intmain(intargc,char*argv[])
{
printf(
"正在随机制造一个文本文件d:\test.txt... ");
MakeFile(
"d:\test.txt");
printf(
"正在进行磁盘文件排序,排序文件d:\sort.text... ");
fmerge(
"d:\sort.txt","d:\test.txt",TOP_SIZE,RECORD_SIZE,CompRecord);
printf(
"磁盘文件排序完毕! ");
system(
"pause");
return0;
}
//---------------------------------------------------------------------------

如有错误,或者你有什么好的建议请来信:maozefa@hotmail.com

发现代码贴上去总是走样,文件路径‘\\’也成了‘\’,‘\n’也没了,MakeFile的2句写记录语句应该分别是,不然,测试会出问题:

fprintf(f, "%s\n", TOPSTRING);

fprintf(f, "%12ld %12ld %12ld %12ld\n", v[0], v[1], v[2], v[3]);

分享到:
评论

相关推荐

    C语言版数据结构幻灯片

    9. **排序和搜索算法**:冒泡排序、选择排序、插入排序、快速排序、归并排序等是常见的排序算法,它们对数据结构的理解和实现有着直接影响。二分查找、线性查找等搜索算法也是数据结构课程的重要组成部分。 10. **...

    对文件内容排序

    例如,将文件分片,每个线程处理一部分,最后再合并结果。在C语言中,可以使用`pthread`库实现多线程编程。 6. **文件处理技巧**: - 使用`mmap()`函数可以将文件映射到内存空间,这样可以避免频繁的读写操作,...

    数据结构 C语言描述 课件

    7. **排序算法**:快速排序、归并排序、冒泡排序、选择排序和插入排序是常见的排序算法,它们在不同的场景下有不同的效率表现。 8. **查找算法**:二分查找适用于有序数组,哈希表提供快速查找但需要额外空间,线性...

    大数据平台构建:MapReduce运行原理.pptx

    在map端,map的输出首先被写入内存缓冲区,当缓冲区接近满时,数据会被溢写到磁盘,并根据预设的分区函数进行分区,每个分区的数据可以进行排序和可选的合并。多个溢出文件在map端被归并为一个大文件,供reduce任务...

    《数据结构(Java版)》一书的幻灯片

    《数据结构(Java版)》一书的幻灯片涵盖了数据结构的基础理论和Java实现,是深入理解数据结构和算法的重要资源。以下是根据幻灯片章节名称(ch7.ppt、ch27.ppt、ch23.ppt、ch1.ppt、ch18.ppt、ch15.ppt、ch16.ppt、...

    杭电计算机学院复习资料

    3. **排序与查找**:掌握各种排序算法(冒泡、插入、选择、快速、归并、堆排序等)和查找算法(顺序查找、二分查找、哈希查找)。 4. **高级数据结构**:如堆、队列、图的遍历算法、图的最小生成树(Prim和Kruskal...

    数据结构分章练习习题与答案

    排序是将一组数据按特定顺序排列的过程,常见的排序算法有冒泡排序、插入排序、选择排序、快速排序、归并排序等。查找算法则是在已排序或未排序的数据中寻找特定元素,如二分查找、哈希查找。习题将涵盖各种排序算法...

    严蔚敏数据结构 Share Accelerator.zip

    6. **排序算法**:包括冒泡排序、选择排序、插入排序、快速排序、归并排序、堆排序等,它们的目标是重新排列元素,使得元素按照特定顺序排列。 7. **动态规划**和**贪心策略**:在解决最优化问题时,动态规划通过子...

    云南大学831数据结构与操作系统考试大纲 .pdf

    10. **排序**:掌握多种排序算法,如插入排序(直接插入、折半插入、2 路插入、希尔排序)、冒泡排序、快速排序、选择排序(简单选择、树选择、堆排序)、归并排序和基数排序。 **操作系统部分:** 操作系统这部分...

    北大数据结构PPT课件

    常见的排序算法有冒泡排序、选择排序、插入排序、快速排序、归并排序和堆排序。 11. 查找:在数据集中寻找特定元素。二分查找法适用于已排序的数组,哈希表提供快速的查找、插入和删除操作。 六、哈希表与散列表 12...

    C程序:一堆C程序

    排序算法有冒泡排序、选择排序、插入排序、快速排序、归并排序等;查找算法有顺序查找、二分查找、哈希查找等。 3. **数据结构**:数据结构是组织和管理数据的方式,包括数组、链表、栈、队列、树(二叉树、红黑树...

    计算机面试资料总结

    - **归并排序**:采用分治法将数组分成两半,递归地进行排序。 - **堆排序**:利用二叉堆数据结构进行排序。 - **拓扑排序**:对于有向无环图进行排序。 - **计数排序**:适用于一定范围内的整数排序。 **6. Hash**...

    整理的面试题.rar

    - **排序和查找算法**:快速排序、归并排序、插入排序、二分查找等,了解它们的时间复杂性和适用场景。 2. **计算机网络**: - **TCP/IP五层模型或OSI七层模型**:理解每一层的功能和协议,如TCP、UDP、HTTP、FTP...

    研究生入学试题

    设计一个递归算法,对单链表进行归并排序。基本思想是将链表分成两半,递归地排序这两半,然后合并已排序的部分。实现时需要注意边界条件和递归终止条件。 ### 操作系统知识点解析 #### 二、操作系统部分 **6. ...

    《大数据平台搭建与配置管理》期末试题试卷及答案.docx

    - 在MapReduce中,一个存储在分布式文件系统中的大规模数据集会被切分成许多独立的数据块,每个数据块作为一个输入分片。 28. **Reduce函数的任务** - Reduce函数的任务是将输入的一系列具有相同键的键值对以某种...

    2016年软件评测师答案

    归并排序的时间复杂度为O(nlog2n),并且它是稳定的排序算法,能够保持相同元素之间的原有顺序。 ##### 18. 树结构 **题目:** 对于一般的树结构,可以采用孩子—兄弟表示法,即每个结点设置两个指针域,一个指针...

    2021-2022计算机二级等级考试试题及答案No.17508.docx

    - **知识点**: 归并排序算法需要额外的内存空间来保存临时数组。 - **应用场景**: 数据排序算法的选择依据之一。 ### 19. JSP标准动作 - **知识点**: `&lt;jsp:useBean&gt;`标签用于创建一个Bean的新实例,并将其存储在...

    觅职渣记-互联网技术类笔试面试总结

    将两个排序好的链表归并** 将两个已排序的链表合并成一个排序链表,可以使用递归或迭代的方法。 ##### 图 **1. 基本知识** - **无向图**:边没有方向。 - **有向图**:边有方向。 - **权重**:边可以带有权重。...

    代码面试最常用的10大算法完整版

    - **归并排序**:同样采用分治法,将数组拆分为两半,分别排序后再合并,保证稳定性。 - **堆排序**:利用完全二叉树结构,通过调整堆来实现排序,能在O(n log n)的时间复杂度内完成。 2. **搜索算法**: - **二...

    程序员面试之葵花宝典

    算法是解决问题的关键,面试中常考的包括排序算法(如冒泡、快速、归并排序)、查找算法(如二分查找、哈希查找)和动态规划等。数据结构如数组、链表、栈、队列、树(二叉树、AVL树、红黑树)、图和哈希表等的理解...

Global site tag (gtag.js) - Google Analytics