一个文件里,有一堆int,把它们排序一下,输出到另外一个文件。
这个问题很简单了,把int读入内存,排序一下,输出到文件。
但是,如果加个条件:数据量巨大,内存无法容纳,那这个问题该怎么解决呢?
嗯,直接说答案:
1) 按内存能放下的规模,顺序读入一批批的数据,排序,输出到不同的文件
2) 现在得到一堆文件,每个文件里是排好序的
3) 对这些文件进行两两归并,就是把两个各自有序的文件,归并到一个有序的文件里
4) 最后得到一个文件
下面是代码:
假设int存放的格式是文本格式,一行一个。
private void button3_Click(object sender, EventArgs e)
{
string from=@"e:\data.txt";
string to=@"e:\target.txt";
SortBigFile(from, to);
Console.WriteLine(GetLineOf(from, 123456789));
}
private void SortBigFile(string from, string to)
{
//读入内存排序的int个数
//2.5e7个int,占内存约 100M
const int BatchRead = 25000000;
//临时文件夹
const string TempDir = @"e:\temp\";
int fileIndex = 0;
StreamReader reader = new StreamReader(from);
try
{
int[] data=new int[BatchRead];
int count = 0;
string line;
while((line=reader.ReadLine())!=null) {
data[count++]=int.Parse(line);
if(count>=BatchRead) {
//读了一批
//排序
Array.Sort(data,0,count);
//写文件
string file=TempDir+"temp"+(++fileIndex)+".txt";
WriteFile(data,count,file);
//继续下一批
count = 0;
}
}
if (count > 0)
{
//余下的不够一批的
Array.Sort(data, 0, count);
//写文件
string file = TempDir + "temp" + (++fileIndex) + ".txt";
WriteFile(data, count, file);
}
}
finally
{
reader.Close();
}
//开始归并排序
string Temp=TempDir+"Temp.txt";
//归并,直到只剩一个文件
while (fileIndex > 1)
{
int c = 0;
for (int i = 1; i <= fileIndex; i += 2)
{
int b = i + 1;
if (b <= fileIndex)
{
MergeFile(TempDir + "temp" + i + ".txt", TempDir + "temp" + b + ".txt", Temp);
c++;
string t = TempDir + "temp" + c + ".txt";
if (File.Exists(t))
{
File.Delete(t);
}
File.Move(Temp, t);
}
else
{
//最后一个,落单了
c++;
string t=TempDir+"temp"+c+".txt";
if(File.Exists(t)) {
File.Delete(t);
}
File.Move(TempDir+"temp"+i+".txt",t);
}
}
fileIndex = c;
}
//归并到一个文件了
File.Move(TempDir + "temp1.txt", to);
}
private void WriteFile(int[] data, int count,string file)
{
if (File.Exists(file))
{
File.Delete(file);
}
StreamWriter writer = new StreamWriter(file, false);
try
{
for (int i=0;i< int.Parse(lb))
{
t.WriteLine(la);
la = ra.ReadLine();
}
else
{
t.WriteLine(lb);
lb = rb.ReadLine();
}
}
else if (la != null)
{
t.WriteLine(la);
la = ra.ReadLine();
}
else
{
//lb!=null
t.WriteLine(lb);
lb = rb.ReadLine();
}
}
t.Flush();
}
finally
{
t.Close();
}
}
finally
{
rb.Close();
}
}
finally
{
ra.Close();
}
}
private string GetLineOf(string file, int n)
{
StreamReader reader = new StreamReader(file);
try
{
string line=null;
for (int i = 0; i < n; i++)
{
line = reader.ReadLine();
if (line == null)
{
break;
}
}
return line;
}
finally
{
reader.Close();
}
}