目录
引言
外排序与内排序的不同
1. 基本概念
代码功能概述
代码详细分析
1. 数据生成函数 CreateData
2. 比较函数 compare (用于qsort函数)
3. 读取数据并排序到文件的函数 ReadDataSortToFile
4. 文件归并函数 MergeFile
5. 主函数 main
总结
在数据处理的过程中,我们经常会遇到需要对大量数据进行排序的情况。当数据量过大,无法一次性全部加载到内存中时,我们可以采用外部排序的方法,其中文件归并是一种常见的实现方式。本文将详细介绍一段用于文件归并的代码,该代码通过分块排序和逐步归并的方式,最终实现对大量数据的排序。
这段代码的主要功能是对一个包含大量整数数据的文件进行排序。具体步骤如下:
CreateData
void CreateData()
{
// 数据个数
int n = 1000000;
const char* file = "data.txt";
FILE* fin = fopen(file, "w");
if(fin == NULL)
{
perror("fopen fin fail!");
exit(1);
}
for(int i = 0; i < n; i++)
{
int x = rand() + i;
fprintf(fin, "%d\n", x);
}
fclose(fin);
}
data.txt
。fopen
函数以写入模式打开文件,如果打开失败则输出错误信息并退出程序。rand()
函数生成随机整数,并使用 fprintf
函数将整数写入文件。fclose
函数关闭文件。compare
(用于qsort函数)int compare(const void* a, const void* b)
{
return (*(int*)a - *(int*)b);
}
qsort
函数所需的比较函数,用于比较两个整数的大小。a
小于 b
,则返回负数;如果 a
等于 b
,则返回 0;如果 a
大于 b
,则返回正数。ReadDataSortToFile
// 返回实际读到的数据个数,若没有则返回 0
int ReadDataSortToFile(FILE* fout, int n, const char* file1)
{
int x = 0;
int* a = (int*)malloc(sizeof(int) * n);
if(a == NULL)
{
perror("malloc fail!");
exit(1);
}
// 想读取 n 个数据,如果遇到文件结束,应该读到 j 个
int j = 0;
for(int i = 0; i < n; i++)
{
if(fscanf(fout, "%d", &x) == EOF)
break;
a[j++] = x;
}
if(j == 0)
{
free(a);
return 0;
}
// 排序
qsort(a, j, sizeof(int), compare);
FILE* fin = fopen(file1, "w");
if(fin == NULL)
{
free(a);
perror("fopen fin fail!");
exit(1);
}
// 写入 file1
for(int i = 0; i < j; i++)
{
fprintf(fin, "%d\n", a[i]);
}
free(a);
fclose(fin);
return j;
}
fout
中读取最多 n
个整数,并将这些整数存储在动态分配的数组 a
中。qsort
函数对数组 a
进行排序。file1
,将排序后的数组元素写入该文件。MergeFile
void MergeFile(const char* file1, const char* file2, const char* mfile)
{
FILE* fin1 = fopen(file1, "r");
if(fin1 == NULL)
{
perror("fopen fin1 fail!");
exit(1);
}
FILE* fin2 = fopen(file2, "r");
if(fin2 == NULL)
{
perror("fopen fin2 fail!");
exit(1);
}
FILE* mfin = fopen(mfile, "w");
if(mfin == NULL)
{
perror("fopen mfin fail!");
exit(1);
}
int x1 = 0;
int x2 = 0;
int ret1 = fscanf(fin1, "%d", &x1);
int ret2 = fscanf(fin2, "%d", &x2);
while(ret1 != EOF && ret2 != EOF)
{
if(x1 < x2)
{
fprintf(mfin, "%d\n", x1);
ret1 = fscanf(fin1, "%d", &x1);
}
else
{
fprintf(mfin, "%d\n", x2);
ret2 = fscanf(fin2, "%d", &x2);
}
}
while(ret1 != EOF)
{
fprintf(mfin, "%d\n", x1);
ret1 = fscanf(fin1, "%d", &x1);
}
while(ret2 != EOF)
{
fprintf(mfin, "%d\n", x2);
ret2 = fscanf(fin2, "%d", &x2);
}
fclose(fin1);
fclose(fin2);
fclose(mfin);
}
file1
和 file2
归并到一个新文件 mfile
中。file1
和 file2
以读取模式打开,mfile
以写入模式打开。file1
和 file2
中分别读取一个整数,比较它们的大小,将较小的整数写入 mfile
中,并继续从相应的文件中读取下一个整数。mfile
中。main
int main()
{
srand((unsigned int)time(NULL));
// 造数据
// CreateData();
const char* file1 = "file1.txt";
const char* file2 = "file2.txt";
const char* mfile = "mfile.txt";
FILE* fout = fopen("data.txt", "r");
if(fout == NULL)
{
perror("fopen fout fail!");
exit(1);
}
int m = 100000;
ReadDataSortToFile(fout, m, file1);
ReadDataSortToFile(fout, m, file2);
while(1)
{
MergeFile(file1, file2, mfile);
// 删除 file1 和 file2
remove(file1);
remove(file2);
// 重命名 mfile 为 file1
rename(mfile, file1);
// 当再去读取数据,一个都读不到,说明已经没有数据了
// 已经归并完成,归并结果在 file1
int n = 0;
if((n = ReadDataSortToFile(fout, m, file2)) == 0)
{
break;
}
}
fclose(fout);
return 0;
}
data.txt
。ReadDataSortToFile
函数将数据分成两个小块,分别排序并保存到 file1.txt
和 file2.txt
中。MergeFile
函数将 file1.txt
和 file2.txt
归并到 mfile.txt
中,然后删除 file1.txt
和 file2.txt
,将 mfile.txt
重命名为 file1.txt
。ReadDataSortToFile
函数尝试读取数据到 file2.txt
中,如果读取的数据个数为 0,则说明所有数据已经归并完成,退出循环。通过分块排序和逐步归并的方式,这段代码实现了对大量数据的排序,避免了一次性将所有数据加载到内存中。这种方法在处理大规模数据时非常有效,可以在有限的内存资源下完成排序任务。