Search code examples
c++stringfilewhile-loopfstream

Nested while loop with file c++ issues


I have to create a program that takes strings in files of group A and compares them with those of the group B, creating a video output and a result equal to the output video file. Group files are structured like this: Jhon Smith'\n'Fergus McDonald'\n'Elizabeth Harris'\n'. I have problems with nested while loops, the last that checks if the name of A is equal to that of B seems to work, but the other two are not, the first time the inner loop works but then the other not, the if condition does not seem to work more and repeats all the names of the files in the number of file elements and vice versa for the second set of nested while loops, i do not understand what is wrong

#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <fstream>

using namespace std;

int main() {
    string As, Bs, Cs;
    ifstream A("Group_A.txt");
    ifstream B("Group_B.txt");
    ofstream C("Result.txt");

    if(!A)
    {
        cout<<"Group A does not exist!";
        return -1;
    }
    if(!B)
    {
        cout<<"Group B does not exist!!";
        return -1;
    }
    if(!C)
    {
        cout<<"Failed to create file!!";
        return -1;
    }

    C<<"Elements in A but not in B:"<<endl;
    while(getline (B,Bs))
    { 
      while(getline (A,As))
      { 
        if (As != Bs)
        {
            C<<As<<endl;
        }
      }
      A.clear();
      A.seekg(0);
    }
    A.close();
    B.close();
    C<<endl;

    A.open("Group_A.txt");
    B.open("Group_B.txt");
    C<<"Elements in B but not in A:"<<endl;
    while(getline (A,As))
    {
      while(getline (B,Bs))
      { 
        if (Bs != As)
        {
            C<<Bs<<endl;
        } 
      }
      B.clear();
      B.seekg(0);
    }
    A.close();
    B.close();
    C<<endl;

    A.open("Group_A.txt");
    B.open("Group_B.txt");
    C<<"Elements present in both A and B:"<<endl;
    while(getline (A,As))
    {
      while(getline (B,Bs))
      { 
        if (As == Bs)
        {
            C<<Bs<<endl;
        } 
      }
      B.clear();
      B.seekg(0);
    }

    A.close();
    B.close();
    C<<endl;
    C.close();

    ifstream res("Result.txt");
    while(res >> Cs)
    cout<<Cs<<endl;

     system ("PAUSE");
      return 0;
}

Solution

  • The problem:

    foreach B in file B
        foreach A in file A
            if (A != B)
                output A 
    

    Say file A contains 1,2,3 and file B contains 2,3,4

    The tests will look like:

    2 != 1: true, output 1
    2 != 2: false, no output
    2 != 3: true, output 3
    3 != 1: true, output 1
    3 != 2: true, output 2
    3 != 3: false, no output
    4 != 1: true, output 1
    4 != 2: true, output 2
    4 != 3: true, output 3
    

    The output file will contain 1,3,1,2,1,2,3

    What you want to do is build the set intersection of file A and B (let's call it AnB). This give you your third loop. Everything in both A and B.

    Compare AnB with A. Everything in A and not in AnB cannot be in B, therefore. is in A and not B. Repeat for B

    Or you can just call std::set_intersection and std::set_difference

    #include <iostream>
    #include <fstream>
    #include <set>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    // Not elegant, but it's pretty much what the OP was using
    // and this isn't a question about proper file handling.
    // Optimize as needed
    bool readFile(const string &filename, set<string> &lines)
    {
        ifstream input(filename.c_str());
    
        if (!input) // this will fail in a few cases
        {
            cout << "Group A does not exist!" << endl;
            return false;
        }
    
        string line;
    
        while (getline(input, line))
        {
            lines.insert(line);
        }
        return true;
    }
    
    void writeFile (ofstream & out,
                    std::vector<string> & data,
                    std::vector<string>::iterator stop,
                    const std::string &message)
    {
        // resizing the vectors the their actual used size makes output easy.
        data.resize(stop - data.begin());
        out << message << endl;
        for (std::vector<string>::iterator it = data.begin(); it != data.end(); ++it)
        {
            out << *it << endl;
        }
    }
    
    int main()
    {
        set<string> Group_A;
        set<string> Group_B;
        ofstream Result("Result.txt");
    
        readFile("Group_A.txt", Group_A);
        readFile("Group_B.txt", Group_B);
    
        // Danger! Danger! Blows up if you do not preallocate
        // enough storage in vectors! Sized for worst case
        std::vector<string> AnB(Group_A.size() + Group_B.size());
        // really this should just be size of the larger list, but I'm lazy
        std::vector<string> AnotB(Group_A.size()); //sized for no intersection
        std::vector<string> BnotA(Group_B.size()); //sized for no intersection
    
        // use this to catch the true vector size after the std::set_* calls
        std::vector<string>::iterator stop;
    
        stop = std::set_intersection(Group_A.begin(), Group_A.end(),
                                     Group_B.begin(), Group_B.end(),
                                     AnB.begin());
        writeFile (Result, AnB, stop, "Intersection:");
    
        stop = std::set_difference(Group_A.begin(), Group_A.end(),
                                   Group_B.begin(), Group_B.end(),
                                   AnotB.begin());
        writeFile (Result, AnotB, stop, "A not B:");
    
        // note the exchange of A and B on the std::set_difference call
        stop = std::set_difference(Group_B.begin(), Group_B.end(),
                                   Group_A.begin(), Group_A.end(),
                                   BnotA.begin());
        writeFile (Result, BnotA, stop, "B not A:");
    }