问题
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?
回答1:
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.)
回答2:
I made it to work by adding a new function that replays the content so now the code is:
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;
for(int i = 0; i < countRowsOfDb(); i++)
{
if(studentsArray[i]->id != term)
{
Student studentTemp(studentsArray[i]->id, studentsArray[i]->name, studentsArray[i]->grade, studentsArray[i]->points, studentsArray[i]->type);
replaceRow(studentTemp);
}
}
cout << "\nObject deleted!\n";
}
and the replace function is:
void replaceRow(Student student)
{
ofstream outDb("files/students.dat", ios::out);
if(!outDb)
{
cerr << "File could no be opened!\n";
}
outDb << student.getId() << ' ' << student.getName() << ' ' << student.getGrade()
<< ' ' << student.getPoints() << ' ' << student.getType() << endl;
outDb.close();
}
来源:https://stackoverflow.com/questions/11014929/delete-specific-line-from-file