Reading duplicated objects on files C++ - c++

I'm having some trouble with creating a file system to my program in C++. Well, I am creating a new student, and, if he is the last object that I'm creating before saving the files and closing the program, it gets duplicated. For example, two objects: Daniel, Paul. It shows just the last one duplicated: Daniel, Paul, Paul - in the file.txt.
Here is some code of mine:
FILE READING:
ifstream file;
file.open("file.txt");
while (1)
{
Student *p = new Student();
if (file.eof() || file.bad() || file.fail())
{
break;
}
getline(file, ALLTHESTRINGVARIABLES);
p->STRINGVARIABLES = ALLTHESTRINGVARIABLES;
file >> ANOTHERVARIABLES;
p->NOTSTRINGVARIABLES = ANOTHERVARIABLES;
students.push_back(p);
}
file.close();
FILE WRITING:
fstream file;
file.open("file.txt", ios::out | ios::trunc);
for(unsigned int i = 0; i < students.size(); i++){
file << students[i]->VARIABLEEXAMPLE << endl;
}
file.close();
Thank you!!

The eof(), bad(), fail() will only return true after a try to read some bytes from the file without success. So, put the if verification after the getline().
And just make the new instance of the Student after this if to avoid a memory leak.
Like that:
while (1)
{
getline(file, ALLTHESTRINGVARIABLES);
if (file.eof() || file.bad() || file.fail())
break;
Student *p = new Student();
p->STRINGVARIABLES = ALLTHESTRINGVARIABLES;
file >> ANOTHERVARIABLES;
p->NOTSTRINGVARIABLES = ANOTHERVARIABLES;
students.push_back(p);
}

eof() will not return true until you've actually tried to read beyond the end of the file which is why you see the last line duplicated. The getline will fail and ALLTHESTRINGVARIABLES will contain the values from the latest successful read. You should instead check if the extraction actually succeeded before storing a new Student.
Since you mix unformatted and formatted input, you should also remove the newline (or whatever character you use to separate the records) from the stream.
Change the loop to:
if (std::ifstream file("file.txt"); file)
{
while (std::getline(file, ALLTHESTRINGVARIABLES) >> ANOTHERVARIABLES)
{
// remove the newline or whatever whitespace char you use as record separator
file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
Student *p = new Student;
p->STRINGVARIABLES = ALLTHESTRINGVARIABLES;
p->NOTSTRINGVARIABLES = ANOTHERVARIABLES;
students.push_back(p);
}
}
I also suggest that you don't store pointers (unless you use dynamic dispatch) but instead store the actual Student objects in a std::vector<Student>. If dynamic dispatch is used, then store std::unique_ptr<Student> in the vector.

Related

C++ how to remove all chars and special characters from a file

I have seen how to remove specific chars from a string but I am not sure how to do it with a file open or if you can even do that. Basically a file will be open with anything in it, my goal is to remove all the letters a-z, special characters, and whitespace that may appear so that all that is left is my numbers. Can you easily remove all chars rather than specifying a,b,c etc when the file is open or would I have to convert it to a string? Also would it be better to do this in memory?
My code this far as is follows:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main() {
string filename;
cout << "Enter the name of the data file to open" << endl;
cin >> filename >> endl;
ofstream myfile;
myfile.open(filename);
if (myfile.is_open()) { //if file is open then
while(!myfile.eof()){ //while not end of file
//remove all chars, special and whitespace
}
}
else{
cout << "Error in opening file" << endl;
}
return 0;
}
Preliminary remarks
If I understand well, you want to keep only the numbers. Maybe it's easier to retain chars that are ascii numbers and eliminate the others rather than eliminate a lot of other chars classes and hope that the remainder is only numbers.
Also never loop on eof to read a file. Loop on the stream instead.
finally, you should read from an ifstream and write to an ofstream
First approach: reading strings
You can read/write the file line by line. You need enough memory to store the largest line, but you benefit from buffering effect.
if (myfile.is_open()) { //if file is open then
string line;
while(getline(myfile, line)){ //while succesful read
line.erase(remove_if(line.begin(), line.end(), [](const char& c) { return !isdigit(c); } ), line.end());
... // then write the line in the output file
}
}
else ...
Online demo
Second approach: reading chars
You can read/write char by char, which gives very flexible option for handling individual characters (toggle string flags, etc...). You also benefit from buffering, but you have function call overhaead for every single char.
if (myfile) { //if file is open then
int c;
while((c = myfile.get())!=EOF){ //while succesful read
//remove all chars, special and whitespace
if (isdigit(c) || c=='\n')
... .put(c); // then write the line in the output file
}
}
else ...
Online demo
Other approaches
You could also read a large fixed size buffer, and operate similarly as with the strings (but don't eliminate LF then). The advantage is that the memory need is not impacted by some very large lines in the file.
You could also determine the file size, and try to read the full file at once (or in very large chunks). You'd then maximize performance at the cost of memory consumption.
This is just an example in order to extract all chars you want from a file with a dedicated filter:
std::string get_purged_file(const std::string& filename) {
std::string strbuffer;
std::ifstream infile;
infile.open(filename, std::ios_base::in);
if (infile.fail()) {
// throw an error
}
char c;
while ((infile >> c).eof() == false) {
if (std::isdigit(c) || c == '.') {
strbuffer.push_back(c);
}
}
infile.close();
return strbuffer;
}
Note: this is just an example and it has to be subject to optimizations. Just to give you an idea:
Read more than one char at time, (with a proper buffer).
Reserve memory in string.
Once you have the buffer "purged" you can overwrite your file on save the content into another file.

My alphabetical string organizer won't save into a file correctly

I have a function that I want to take a file, look at the words in the file, position them in alphabetical order and replace the file with that. So far, I have it to get the words in alphabetical order. The problem is, is that it only saves the last word to the file.
Here is my current code:
void thingy(string Item)
{
fstream File; // Open the file, save the sorted words in it, and then close it.
File.open("Data/Alphabet.txt", ios::out | ios::in);
File << Item;
File.close();
}
void Alphabetical_Order(string Text_File)
{
fstream file;
file.open(Text_File); // Open the file that we are going to organize
std::set<std::string> sortedItems; // The variable that we will store the sorted words in
fstream words; // To see how many words are in the file
words.open(Text_File);
int Words = 0;
do
{
string word;
words >> word;
Words++;
} while (!words.eof());
for (int i = 1; i <= Words; ++i) // Set a loop to take the words out of the file and put them in our variable
{
std::string Data;
file >> Data;
Data = Data + " ";
sortedItems.insert(Data);
}
std::for_each(sortedItems.begin(), sortedItems.end(), thingy);
}
Does anyone know how to fix this?
When you open the fstream in thingy, try opening with ios::ate flag as well. This will allow you to append your text to the file, rather than rewrite every time you call the function.
That being said, you should not be opening and closing the file every time you call the function. Maybe pass a reference to a fstream that gets managed from outside the function thingy.
I hope this helps.
Aiden already pointed out the main problem in your code (+1).
For the records, I'd like however to propose you a more efficient output using the powerfull ostream_iterator: it avoids opening/closing the output file a lot of times and doesn't need trailing spaces in all strings. This being said, I'd suggest to void the unnecessary double pass read as well:
void Alphabetical_Order(string Text_File)
{
ifstream file(Text_File); // Open in read mode
std::string word;
std::set<std::string> sortedItems; // The variable that we will store the sorted words in
while (file >> word) { // just read the file in one pass
sortedItems.insert(word); // to populate the set
}
ofstream ofs("alphasorted.txt");
std::copy(sortedItems.begin(), sortedItems.end(), std::ostream_iterator<std::string>(ofs, " "));
}

Reading in from a txt file. Trouble parsing info

I want to read in scores from a txt file. The scores are going into a struct.
struct playerScore
{
char name[32];
int score, difficulty;
float time;
};
the text file looks like this
Seth 26.255 40 7
as one line, where each item is followed by a tab. (Name\t time\t score\t difficulty\n)
When I begin to read in the text, I don't know how to tell the program when to stop. The scores file could be any number of lines or score entries. This is what I have attempted.
hs.open("scores.txt", ios_base::in);
hs.seekg(0, hs.beg);
if (hs.is_open())
{
int currpos = 0;
while (int(hs.tellg()) != int(hs.end));
{
hs>> inScore.name;
hs >> inScore.time;
hs >> inScore.score;
hs >> inScore.difficulty;
hs.ignore(INT_MAX, '\n');
AllScores.push_back(inScore);
currpos = (int)hs.tellg();
}
}
I'm trying to make a loop that will read in a line of code into a temp struct for the data, then push that struct into a vector of structs. Then update the currpos variable with the current location of the input pointer. However, the loop just gets stuck on the condition and freezes.
There are a multitude of ways to do this, but the following is likely what you're looking for. Declare a free-operator for extracting a single-line definition of a player-score:
std::istream& operator >>(std::istream& inf, playerScore& ps)
{
// read a single line.
std::string line;
if (std::getline(inf, line))
{
// use a string stream to parse line by line.
std::istringstream iss(line);
if (!(iss.getline(ps.name, sizeof(ps.name)/sizeof(*ps.name), '\t') &&
(iss >> ps.time >> ps.score >> ps.difficulty)))
{
// fails to parse a full record. set the top-stream fail-bit.
inf.setstate(std::ios::failbit);
}
}
return inf;
}
With that, your read code can now do this:
std::istream_iterator<playerScore> hs_it(hs), hs_eof;
std::vector<playerScore> scores(hs_it, hs_eof);
I dont think that you can just >> from your file. Do you think it will take everything till \t? :)
You can try to take for example token with strtok()
I guess it can use '\t' to split string and take for each variable via this function needed part of string
In case if it strtok() doesnt work that way i guess you can just copy till '\t' in sub-loop
You can do like this
playerScore s1;
fstream file;
file.open("scores.txt", ios::in | ios::out);
while(!file.eof()) //For end of while loop
{
file.read(s1, sizeof(playerScore));//read data in one structure.
AllScores.push_back(s1);
}

can't open a file by reading its name from another file in c++

Hi I am trying to read a file name from another file and then read it. But I can only read the first file which contains the name of the second file I want to open. Here is how I am doing it..
int main()
{
freopen("input1.txt","r",stdin);
while(cin>>fileName>>source>>destination)
{
//reads perfectly
char file[100];
for(int i=0;i<(int)fileName.size();i++)
file[i] = fileName[i];
file[(int)fileName.size()] = NULL;
freopen(file,"r",stdin);
mp.clear();
mp1.clear();
for(int i=0;i<cityNumber;i++)
adj[i].clear();
cityNumber = 0;
while(cin>>city1>>city2>>distanc)
{
//doesn't read
}
}
Your code uses overly complicated constructs. Why not just do it the straightforward C++ way:
#include <fstream>
int main()
{
std::ifstream input1("input1.txt");
while(input1 >> fileName >> source >> destination)
{
std::ifstream file(fileName.c_str());
mp.clear();
mp1.clear();
for(int i=0;i<cityNumber;i++)
adj[i].clear();
cityNumber = 0;
while(file >> city1 >> city2 >> distanc)
{
//work with values
}
}
The man page for freopen says:
The freopen() function opens the file whose name is the string pointed to by path and associates the stream pointed to by stream with it. The original stream (if it exists) is closed.
Hence,
freopen("input1.txt","r",stdin);
closes the stdin stream, and
freopen(file,"r",stdin);
finds no open stream to associate to file.
You should probably plain fopen the file input.txt and read from it to leave stdin for the final target.
A few things to consider:
do not use freopen/fopen family of files in C++ (unless it's a really special case)
do not mix std::iostreams with fopen family of files (freopen should not be used here)
these two points will fix your particular error (as #Angew pointed out)
prefer std::string with std::getline instead of char file[100]; This avoids buffer overflow if reading over 100 chars (I believe you don't check for errors) and simplifies your code.
prefer iterators over iterating by index.
That means instead of:
for(int i=0;i < cityNumber;i++)
adj[i].clear();
you could/should write:
// C++11
for(auto& city: adj)
city.clear();
// C++98
for(<type of adj>::iterator city = adj.begin(); city != adj.end(); ++city)
city->clear();

Why doesn't this change the .txt file?

I'm trying to edit a text file to remove the vowels from it and for some reason nothing happens to the text file. I think it may be because a mode argument needs to be passed in the filestream.
[SOLVED]
Code:
#include "std_lib_facilities.h"
bool isvowel(char s)
{
return (s == 'a' || s == 'e' || s =='i' || s == 'o' || s == 'u';)
}
void vowel_removal(string& s)
{
for(int i = 0; i < s.length(); ++i)
if(isvowel(s[i]))
s[i] = ' ';
}
int main()
{
vector<string>wordhold;
cout << "Enter file name.\n";
string filename;
cin >> filename;
ifstream f(filename.c_str());
string word;
while(f>>word) wordhold.push_back(word);
f.close();
ofstream out(filename.c_str(), ios::out);
for(int i = 0; i < wordhold.size(); ++i){
vowel_removal(wordhold[i]);
out << wordhold[i] << " ";}
keep_window_open();
}
Reading and writing on the same stream results in an error. Check f.bad() and f.eof() after the loop terminates. I'm afraid that you have two choices:
Read and write to different files
Read the entire file into memory, close it, and overwrite the original
As Anders stated, you probably don't want to use operator<< for this since it will break everything up by whitespace. You probably want std::getline() to slurp in the lines. Pull them into a std::vector<std::string>, close the file, edit the vector, and overwrite the file.
Edit:
Anders was right on the money with his description. Think of a file as a byte stream. If you want to transform the file in place, try something like the following:
void
remove_vowel(char& ch) {
if (ch=='a' || ch=='e' || ch=='i' || ch =='o' || ch=='u') {
ch = ' ';
}
}
int
main() {
char const delim = '\n';
std::fstream::streampos start_of_line;
std::string buf;
std::fstream fs("file.txt");
start_of_line = fs.tellg();
while (std::getline(fs, buf, delim)) {
std::for_each(buf.begin(), buf.end(), &remove_vowel);
fs.seekg(start_of_line); // go back to the start and...
fs << buf << delim; // overwrite the line, then ...
start_of_line = fs.tellg(); // grab the next line start
}
return 0;
}
There are some small problems with this code like it won't work for MS-DOS style text files but you can probably figure out how to account for that if you have to.
Files are sort of like a list, a sequential byte stream. When you open the file you position the file pointer at the very start, every read/write repositions the file pointer in the file with an offset larger than the last. You can use seekg() to move back in the file and overwrite previous content. Another problem with your approach above is that there will probably be some delimiters between the words typically one or more spaces for instance, you will need to handle read/write on these too.
It is much easier to just load the whole file in memory and do your manipulation on that string then rewriting the whole thing back.
Are you sure your while loop is actually executing? Try adding some debugging output to verify that it's doing what you think it is.