Copy .txt data to another .txt file

Started by
4 comments, last by Boooke 12 years ago
Hello, I do not really figure how to solve this problem. I am to make a database in a text file, and I would like to delete specific users in the file without affecting the rest of the file. To do so, I read the user list into a copied text file (Which seems to work somewhat fine), until I find the specific "ID" i want to delete. From here I continue to read the input file but avoid copying it over to the other text-file. It should then stop doing this when it finds another ID tag, but this seems to fail, Also, I want to copy the copied text file back to the original file because of the orignal filename is needed. Here it seems to fail, and the only output copied is "0x28fad4".





This is the function

void erase(std::string ID)
{
std::ifstream inFile("userlist.txt");
std::ofstream outCopy("copy.txt");
std::string buffer;
bool copyDone = false;
while (getline(inFile, buffer))
{
if (buffer == ("ID: " + ID))
{
while (getline(inFile, buffer) && buffer != "ID: ");
copyDone = true;
buffer.clear();
std::cout << "ID Found. Deleting user.";
}
outCopy << buffer;
std::cout << buffer << std::endl;
}
if (copyDone == false)
{
std::cout << "ID not present in userlist.";
outCopy.close();
inFile.close();
remove("copy.txt");
return;
}
inFile.close();
outCopy.close();
inFile.open("copy.txt");
outCopy.open("userlist.txt", std::ios::trunc);
if (outCopy.good())
std::cout << "Hej.";
outCopy << inFile;
inFile.close();
outCopy.close();
//remove("copy.txt");
}


here is userlist.txt, where ID: 111d should be deleted

Hello.
ID: 111d
Test.
ID: 202d
Should be kept safe from harm.


The copy.txt where everything concerning ID: 111d should have been removed ,but not IDs after,

Hello.

The userlist.txt after erase(string) now only contain,

0x28fad4



I guess the read loop inside the first while loop needs another condition, and the way I try to copy input from inFile to inCopy do not work, but I thought it could have been interesting to know, if it was possible to copy from the copied text back again without getline().

Regards,
Boooke
Advertisement
Im not sure if this is the issue but it appears that you are clearing the buffer then latter attempting to write the empty buffer when you find the ID you are looking for.

if (buffer == ("ID: " + ID))
{
while (getline(inFile, buffer) && buffer != "ID: ");
copyDone = true;
buffer.clear();
std::cout << "ID Found. Deleting user.";
}
outCopy << buffer;
std::cout << buffer << std::endl;



Did you mean to put the second part inside an else block?
outCopy << buffer;
std::cout << buffer << std::endl;
Yeah, I actually looked at that and it seemed at bit wrong, but I wasn't sure. It would be a good idea to put it into an else-statement. Thank you for the tip! I completely forgot about that one. But I still do not figure how I faulted the copy-section.


if (buffer == ("ID: " + ID))
{


C++ doesn't do native string concatenation like one would expect:

int main() {
const char* str = "Test: " + 3; // Pointer arithmetic, not concatenation
cout << '"' << str << '"' << endl;
}

Output:
"t: "

The C++ way would probably look something like this:

istringstream ss( buffer );
string prefix, extra;
int value;
ss >> prefix >> value;
getline( ss, extra );
if ( ss && prefix == "ID:" && extra.empty() ) {
// Do something with value
} else {
// Error
}
Given that you appear to require the database to fit in memory, I would recommend reading the database just once at startup. Read it into a record type. When changes are made to the database, add or remove these records from the collection in memory, and then write the data from memory to the file*.
This will be easier than writing multiple routines that trying to add or remove records while streaming the text from the file. It also separates the structure of the file from the logic - you can change the loader / saver without having to modify the way adding, removing, searching or sorting is done internally.


* How and when to write the data depends on how much you care about the data:

  • If you don't care (e.g. test program), you can just write the data when the user quits the program.
  • If you care a little bit about data, write changes immediately after any non-read operation.
  • If you moderately care about data, it is better to write the data to a temporary file first, and use an atomic rename to "replace" the file once you are sure it has been written correctly to disk.
  • If you really care about data loss, you'll use an ACID database to store it.


You don't need to explicitly close std::fstreams. You shouldn't re-use them, as you would need to account for the various error reporting flags. For example, if your streams become invalid (e.g. the filesystem becomes full or some other program has obtained a lock on the file), then you will lose all data in the file. You need much more careful error handling to avoid data loss and corruption.

[edit: I misread your code. You don't appear to require the data to fit into memory. Still, consider the above points]
The plan is to read everything into a dynamic struct at some point. In the beginning would be a good idea. Maybe it would be much quicker to just edit the struct element that needs to be editted and then rewrite the whole "database", instead of having to edit the .txt and then read to the struct? Gonna try that, at least.

I know little of database-structuring. However, I will write the data to a copy.txt every time I change it in the struct and then I will read the copy to the now erased userlist.txt.. I am not sure if that is what you meant. But thank you for the ACID-model idea. If I have time, it would be a great idea to implement, also for exercising use in real projects.

I guess a lot of the erase() function i made is now inefficient, but the parts I need are actually still defunct, since I copying from text to text seems to give me some problems with the input.

This topic is closed to new replies.

Advertisement