I'm just gonna warn you all that my code contains small bits of french and english here and there so if you don't understand a variable or something please do ask and consider it as a free French class haha. Okay so I have a project which consists of creating a timetable for schools to use and I'm in charge of managing the "time" part, ie creating time gaps and rooms for professors to do their classes. In order to make sure all the situations work correctly I need to make sure that 2 classes can't happen at the same time and place. In my function "rajoutcours" which means "AddsClass" in French, I take in several parameters:
In my function I create 3 variables which are heure_start (time at which another class starts), heure_end (time at which another class ends), day and room (the day the other classes happen and the room where they are taking place). I fill in these variables using the string operator "+" property where i convert every character of the line in my txt files (which are the other classes) into 1 lettered strings so I can add them up using the sstream library. Weirdly enough, only one character is converted into a string but the others don't and I really don't see why.
Anyways I know this a lot but I've been trying to find out for days and I don't see where's the issue. Thank you all for your help.
void Utilisateur::rajout_cours(string matiere, string heure_deb, string heure_fin, string date, string salle )
{
ifstream user_in ("Cours.txt"); // txt file where classes are listed
ofstream user_out;
user_out.open("Cours.txt", ofstream::out | ofstream::app);
user_out; //write
string ligne; //line variable which reads the lines of the txt file
string heure_start; // time at which the class starts
string heure_end; // time at which the class ends
string day; // self explanatory
string room; // ie before
int i=0;
int j=0;
int cptr=0;
int a,b,c,d;
stringstream ss; // this is supposed to convert char into string
while (getline(user_in,ligne))
{
for(i=0;i<ligne.size();i++)
{
if(ligne.at(i)=='$' && cptr==0)
{
a=i;
cptr++;
cout << "Premier '$'" << endl; // Keep in mind that the '$' is the delimiter when i write on the txt file, it's also a way to know where I am.
for(j=0;j<a;j++)
{
char tmpc='\0';
string tmps;
tmpc=ligne.at(j);
ss << tmpc;
cout << "Lettre a la case " << j << " : "<< tmpc << endl; //I want to know what's the char in the j-th character of the word
ss >>tmps;
cout << "Lettre string a la case " << j << " : "<< tmps << endl; // I want to display it's string version
heure_start=heure_start+tmps;
}
}
else if(ligne.at(i)=='$' && cptr==1)
{
b=i;
cptr++;
cout << "Deuxieme '$'" << endl;
for(j=a+1;j<(b);j++)
{
char tmpc = '\0';
string tmps;
tmpc=ligne.at(j);
ss << tmpc;// conversion char en string
cout << "Lettre char a la case " << j << " : "<< tmpc << endl; //I want to know what's the char in the j-th character of the word
ss >>tmps;// conversion complète à priori
cout << "Lettre string a la case " << j << " : "<< tmps << endl; // I want to display it's string version
heure_end=heure_end+tmps;
}
}
else if(ligne.at(i)=='$' && cptr==2)
{
c=i;
cptr++;
cout << "3eme '$'" << endl;
for(j=b+1;j<(c);j++)
{
char tmpc='\0';
string tmps="";
tmpc=ligne.at(j);
ss << tmpc;
cout << "Lettre a la case " << j << " : "<< tmpc << endl; //I want to know what's the char in the j-th character of the word
ss >>tmps;
cout << "Lettre string a la case " << j << " : "<< tmps << endl; // I want to display it's string version
room=room+tmps;
}
}
else if(ligne.at(i)=='$' && cptr==3)
{
d=i;
cptr++;
cout << "4eme '$'" << endl;
for(j=c+1;j<(d);j++)
{
char tmpc='\0';
string tmps="";
tmpc=ligne.at(j);
ss << tmpc;
cout << "Lettre char a la case " << j << " : "<< tmpc << endl; //I want to know what's the char in the j-th character of the word
ss >>tmps;
cout << "Lettre string a la case " << j << " : "<< tmps << endl; // I want to display it's string version
day=day+tmps;
}
}
}
}
if(heure_deb==heure_start && heure_fin==heure_end && date==day && salle==room) // I make sure here that the class I'm writing isn't already written in the file and in that case we leave the program.
{
cout << "Impossible d'ajouter un cours ! Un cours de " << matiere <<"a deja lieu à ce moment! Changez d'horaires ou de salles. " << endl;
exit(1);
}
cout <<"ecris" << endl;
user_out << heure_deb << "$"<< heure_fin << "$" << salle << "$" << date << "$" << matiere << endl; // If not, write the new class.
}
I already mentioned that it's very simple to convert a char
to a std::string
by simply using the resp. constructor std::string::string(size_t count, char ch)
:
char c = 'A';
std::string str(1, c); // constructs a string str with one character
However, after I read the question the third time I realized that it should actually work the way the OP tried to do it.
So, for me the actual question resulted in:
Why std::stringstream
cannot be used repeatedly for alternating output/input?
After a short time, I remembered the "stream" in stringstream
.
Well, it can – considering the fact that after filling it with output (e.g. <<
) and emptying it with input (e.g. >>
) it's internal state becomes eof
where next read will result in fail
state. So, (although I still would recommend to use my above described simpler method),
std::stringstream
before re-using it (std::stringstream::clear()
is good for that.)std::stringstream
(by enclosing the construction of stringstream and the input/output statements in an extra pair of braces { }
)A small example:
#include <iostream>
#include <sstream>
#include <string>
int main()
{
{ // "recycle" one instance of std::stringstream
std::stringstream sstr;
for (char c = 'A'; c <= 'C'; ++c) {
std::cout << "c: '" << c << "', ";
sstr << c;
std::string str;
sstr >> str;
std::cout << "str: '" << str << "', "
<< "sstr.good(): " << sstr.good() << ", "
<< "sstr.eof(): " << sstr.eof() << '\n';
sstr.clear();
}
}
{ // one instance per I/O
for (char c = 'A'; c <= 'C'; ++c) {
std::cout << "c: '" << c << "', ";
std::stringstream sstr;
sstr << c;
std::string str;
sstr >> str;
std::cout << "str: '" << str << "'\n";
}
}
// done
return 0;
}
Output:
c: 'A', str: 'A', sstr.good(): 0, sstr.eof(): 1
c: 'B', str: 'B', sstr.good(): 0, sstr.eof(): 1
c: 'C', str: 'C', sstr.good(): 0, sstr.eof(): 1
c: 'A', str: 'A'
c: 'B', str: 'B'
c: 'C', str: 'C'
After thinking twice, I realized that there is another reason why clearing the eof
state is necessary in this specific case:
char
std::string
(which is somehow much more greedy) and reads until delimiter or end-of-file (and it's always the latter what happens).Checked this out:
#include <iostream>
#include <sstream>
int main()
{
// "recycle" one instance of std::stringstream
std::stringstream sstr;
for (char c = 'A'; c <= 'C'; ++c) {
std::cout << "c: '" << c << "', ";
sstr << c;
char cIO;
if (sstr >> cIO) std::cout << "cIO: '" << cIO << "'\n";
else std::cout << "reading cIO failed\n";
}
// done
return 0;
}
Output:
c: 'A', cIO: 'A'
c: 'B', cIO: 'B'
c: 'C', cIO: 'C'
Now, it works even without sstr.clear()
as it never tries to read past the end of file (aka stringstream).
Mentioning the "greedy" reading of operator>>(std::stringstream&, std::string)
brought me to another idea:
What happens if the char
in quest is a delimiter?
#include <iostream>
#include <sstream>
#include <string>
int main()
{
char c = ' ';
std::stringstream sstr;
sstr << c;
std::cout << "c: '" << c << "', ";
std::string str;
sstr >> str;
std::cout << "str: '" << str << "'\n";
std::cout
<< "sstr.good(): " << sstr.good() << '\n'
<< "sstr.eof(): " << sstr.eof() << '\n'
<< "sstr.fail(): " << sstr.fail() << '\n'
<< "sstr.bad(): " << sstr.bad() << '\n';
// done
return 0;
}
Output:
c: ' ', str: ''
sstr.good(): 0
sstr.eof(): 1
sstr.fail(): 1
sstr.bad(): 0
I must admit that (as I never had tried to convert char
to std::string
using a std::stringstream
) I was not aware that std::stringstream
is a that bad choice.