在 c++ 中使用 MergeSort 对文件进行排序

Order files with MergeSort in c++

本文关键字:文件 排序 MergeSort c++      更新时间:2023-10-16

我正在努力实现我自己的MergeSort,但我遇到了一些问题,看看是否有人可以帮助我。

我有一个大文件,其中包含一些与昏迷分开的信息(名称,城市,邮件,telf)。我想应用 mergesort 来排序它,因为我认为客户端计算机一次尝试就不会有那么多内存来执行此操作。

所以,我把它分成MAX_CUSTOMERS行的文件,然后单独排序,直到这里都是正确的,但是当我想获取前两个文件并对它们进行排序时,我遇到了所有问题,我重复了,一个和其他消失了,这是我的代码:

void MergeSort(string file1Name, string file2Name,string name){
printf("Enter MERGE SORT %s AND %sn",file1Name.c_str(),file2Name.c_str());
string temp;
string fileName;
string lineFile1, lineFile2;
bool endFil1 = false, endFil2 = false;
int numCust1 = 0;
int numCust2 = 0;
int x1 = 0, x2 = 0;
ifstream file1;
file1.open(file1Name.c_str());
ifstream file2;
file2.open(file2Name.c_str());
ofstream mergeFile; 
fileName = "customers_" +name +".txt";
cout << "Result file " << fileName << endl;
mergeFile.open("temp.txt");
getline(file1,lineFile1);
getline(file2,lineFile2);
while(!endFil1 && !endFil2){
    if(CompareTelf(lineFile1,lineFile2)==1){
        mergeFile << lineFile1 << endl;
        if(!getline(file1,lineFile1)){
            cout << lineFile1 << endl;
            cout << "1st file end" << endl;         
            endFil1 = true;
        }
    }else{
        mergeFile << lineFile2 << endl;
        if(!getline(file2,lineFile2)){
            cout << lineFile2 << endl;
            cout << "2nd file end" << endl;         
            endFil2 = true;
        }
    }       
}
if(endFil1){
    //mergeFile << lineFile2 << endl;
    while(getline(file2,lineFile2)){
        mergeFile << lineFile2 << endl;
    }
}else{
    //mergeFile << lineFile1 << endl;
    while(getline(file1,lineFile1)){
        mergeFile << lineFile1 << endl;
    }
}
file1.close();
file2.close();
mergeFile.close();
rename("temp.txt",fileName.c_str());
return;
}
Customer SplitLine(string line){
string splitLine;
string temp;
Customer cust;
int actProp = 0;
int number;
istringstream readLineStream(line); //convert String readLine to Stream readLine
while(getline(readLineStream,splitLine,',')){
    if (actProp == 0)cust.name = splitLine;
    else if (actProp == 1)cust.city = splitLine;
    else if (actProp == 2)cust.mail = splitLine;
    else if (actProp == 3)cust.telf = atoi(splitLine.c_str());
    actProp++;
}
//printf("Customer read: %s, %s, %s, %in",cust.name.c_str(), cust.city.c_str(), cust.mail.c_str(), cust.telf);
return cust;
}
int CompareTelf(string str1, string str2){
    Customer c1 = SplitLine(str1);
    Customer c2 = SplitLine(str2);
    if(c1.telf<c2.telf)return 1; //return 1 if 1st string its more important than second, otherwise, return -1
    else return -1;
}
struct Customer{
        string name;
        string city;
        string mail;
        long telf;
};

如果对代码有疑问,请说出来!我试图使用尽可能描述性的varNames!

多谢。

你的代码看起来相当不错,但它有几个缺陷和一个重要的遗漏。

一个小缺陷是缺少Customer结构的初始化 - 您没有为结构提供构造函数,也没有显式初始化cust变量。希望字符串成员由字符串类构造函数正确初始化,但long telf可能会获得任何初始值。

另一个是在拆分输入行时缺乏格式检查。您确定每个输入行具有相同的格式吗?如果行中逗号太多(例如,名称中的逗号),则循环可能会错误地尝试将"电子邮件"数据分配给"telf"成员...
OTOH 如果逗号太少,"telf"成员可能会保持未初始化状态,并带有随机的初始值......
与第一个缺陷一起,此缺陷可能导致输出数据的顺序不正确。

当你使用函数时atoi类似的问题会出现:它返回int但你的变量是long的。我想您选择long类型是因为预期的值范围 - 如果是这样,将输入数据转换为int可能会截断大部分数据!我不确定在这种情况下atoi做什么,它可能会返回转换输入字符串某些初始部分的结果,或者只返回零。这两个值都是错误的,并导致排序不正确,因此最好改用atol

下一个问题是从两个输入文件中读取第一行。您不检查getline()是否成功。如果输入文件为空,则相应的lineFile_num字符串将为空,但endFil_num不会反映这一点 - 它仍将false。因此,您再次比较无效数据。

最后是主要问题。假设 file1 内容"大于"(即:在之后)整个文件 2。然后,存储在lineFile1中的第一行导致CompareTelf()一直返回-1。主循环将整个 file2 复制到输出中,然后...?最后的while()循环从getline(file1,lineFile1)开始,从而丢弃 file1 的第一行!
由记录 (A,C) 和 (B) 组成的文件也会发生类似的结果,这些文件要合并为 (A,B,C):首先读入 A 和 B,然后保存 A 并读入 C,然后保存 B 并检测到文件 2 的结尾。然后while(getline(...))取消内存中的 C 并找到文件 1 的结尾,从而终止循环。记录 C 丢失。
通常,当主合并循环while(!endFil1 && !endFil2)耗尽其中一个文件时,另一个文件的第一行未保存将被丢弃。为了避免这种情况,您需要存储第一次读取的结果:

endFil1 = ! getline(file1,lineFile1);
endFil2 = ! getline(file2,lineFile2);

然后,在主循环之后,开始复制带有未保存行的输入文件的尾部:

while(!endFil1) {
    mergeFile << lineFile1 << endl;
    endFil1 = !getline(file1,lineFile1);
}
while(!endFil2) {
    mergeFile << lineFile2 << endl;
    endFil2 = !getline(file2,lineFile2);
}