Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delete specific line from file

Tags:

c++

line

fstream

I'm trying to delete a specific line by id from a file in C++ and here is my code:

void deleteRow()
{
    ifstream inDb("files/students.dat", ios::in);
    if(!inDb)
    {
        cerr << "File could no be opened!\n";
    }

    cout << countRowsOfDb() << " records." << endl;

    Student *studentsArray[countRowsOfDb()];

    int n = 0;

    while(inDb >> id >> name >> grade >> points >> type)
    {
        studentsArray[n] = new Student(id, name, grade, points, type);
        n++;
    }

    inDb.close();

    for(int i = 0; i < countRowsOfDb(); i++)
    {
        cout << studentsArray[i]->id << " " << studentsArray[i]->name << " " << studentsArray[i]->grade << " "
             << studentsArray[i]->points << " " << studentsArray[i]->type << "\n";
    }

    cout << "\nWhich one you would like to delete? Enter an id: ";

    string term;
    cin >> term;

    ofstream outDb("files/students.dat", ios::out);
    if(!outDb)
    {
        cerr << "File could no be opened!\n";
    }


    for(int i = 0; i < countRowsOfDb(); i++)
    {
        if(studentsArray[i]->id != term)
        {
                outDb << studentsArray[i]->id << " " << studentsArray[i]->name << " " << studentsArray[i]->grade << " "
                      << studentsArray[i]->points << " " << studentsArray[i]->type << "\n";
        }
    }

    outDb.close();

    cout << "\nObject deleted!\n";
}

I create a input file stream and then get all the rows, make it array of objects and show them on the screen then ask which one to delete by typing an id, and when I type the id, I'm trying to put all these elements of the array only without the element with same id, but it don't works, after that there is nothing in the file. Any ideas?

like image 300
fre2ak Avatar asked Jan 24 '26 19:01

fre2ak


1 Answers

What's in countRowsOfDb()? If it opens the file and counts the lines in it (and I don't know what else it could do), then it's not going to find a lot in the final loop, since the creation of the ostream with the same name will have emptied the file.

More generally, this is a very inefficient way of doing things (and could easily fail if there were an error in the format of the file). The best way to handle this is to use an std::vector<Student>, with:

studentVector.push_back( Student( id, name, grade, points, type ) );

in the input loop. In all later loops, studentVector.size() gives the number of entries, or you can use the iterators.

Even better would be to use std::getline on the input, then initialize an std::istringstream to parse each line. This will catch input format errors much more reliably. Something like:

std::string line;
int lineNumber = 0;
while ( std::getline( inDb, line ) ) {
    ++ lineNumber;
    std::istringstream data( line );
    if ( data >> id >> name >> grade >> points >> type ) {
        studentVector.push_back( Student( id, name, grade, points, type ) );
    } else {
        std::cerr << "Format error in lne " << lineNumber << std::endl;
    }
}

Also, it is generally a better idea to write to separate file, then rename it after having verified that the write worked, i.e.:

std::ofstream outDb( "files/students.dat.new" );
//  Do output...
outDb.close();
if ( outDb ) {
    remove( "files/students.dat" );
    rename( "files/students.dat.new", "files/students.dat" );
} else {
    std::cerr << "Write error on output" << std::endl;
}

And of course, any write error should result in a return of EXIT_FAILURE from main. (This is one case where a global variable or a singleton is justified—keeping track of the return code.)

like image 194
James Kanze Avatar answered Jan 27 '26 07:01

James Kanze



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!