Basic C++ program, getline()/parsing a file - c++

I've been tasked with creating a small program that is to parse through a text file and grab necessary info from it. The file is laid out as such
Tuesday*Info5051*10:00*11:00*M3039*Info5064*12:00*3:00*G1001;
Basically it's supposed to store each string in a struct so that I can later retrieve it, but I'm unable to get my program to work (I have a learning disability so things tend to get difficult). Here's my code so far. (I know it's a simple program but I tend to overthink/screw up stuff.) My big problem I've hit so far is that it won't open the file to start. I've saved the file to the bin->debug as well as the main folder of the program. I'm sure I'm using the getline method wrong.
struct Course
{
string _sDay;
string _sName;
string _sCode;
string _iStart;
string _iDuration;
string _sRoom;
};
int main()
{
ifstream fileIn;
fileIn.open("courseLoad.txt");
vector<Course> vCourse;
string str="*";
string line;
if (!fileIn)
{
cout<<"A error has occured, please contact support.";
}
while(!fileIn.eof())
{
for(int i=0; i!= fileIn.eof();i++)
{
//file.getline(entry.part_num, 6, '-');
getline(fileIn,line,'*');
vCourse[i]._sDay =line;
getline(fileIn,line,'*');
vCourse[i]._sName =line;
getline(fileIn,line,'*');
vCourse[i]._sCode = line;
getline(fileIn,line,'*');
vCourse[i]._iStart =line;
getline(fileIn,line,'*');
vCourse[i]._iDuration = line;
getline(fileIn,line,'*');
vCourse[i]._sRoom =line;
cout<<vCourse[i];
}//end for
}
--output to screen here--

There are several issue with this code:
1) That code is missing a return statement or an else statement to prevent the program from continuing its execution in case it cannot open the file:
if (!fileIn)
{
cout<<"A error has occured, please contact support.";
return -1;
}
2) Your getline all operate on the same input stream. You want to read in a line, then parse that line. For example:
// Read in a line
while (getline(fileIn,line))
{
string item;
std::stringstream sstr(line);
// Read in an item
while (getline(sstr, item, "*"))
{
std::cout << item << std::endl;
}
}
3) vCourse size is 0, so you cannot use the [] operator; but you can use push_back to expand the size of the vector and insert an element at the back of the vector:
// Read in a line
while (getline(fileIn,line))
{
string item;
// Default course construction
Course c;
std::stringstream sstr(line);
// Read in an item
getline(sstr,item,'*');
c._sDay = item;
getline(sstr,item,'*');
c._sName = item;
getline(sstr,item,'*');
c._sCode = item;
getline(sstr,item,'*');
c._iStart = item;
getline(sstr,item,'*');
c._iDuration = item;
getline(sstr,item,'*');
c._sRoom = item;
// Save the course into the vector
vCourse.push_back(c);
}
You could also add some more error checking in the above (in case some elements are missing from the line).

One immediate problem that is clear is that you are not actually adding any Course structs into your vector but you are assigning to the elements of them as if you are. For example
vCourse[i]._sDay =line;
but you have not actually added an instanct of a Course struct to the vector at index i. This means you assign to an instance that is not present and that is never good news. What you need prior to this is
Course newItem; // make a new Course object instance
vCourse.push_back(newItem); // This adds the instance to the end of the vector
// Now assign to the members of vCourse[i];
vCourse[i]._sDay =line;
getline(fileIn,line,'*');
vCourse[i]._sName =line;
getline(fileIn,line,'*');
vCourse[i]._sCode = line;
getline(fileIn,line,'*');
vCourse[i]._iStart =line;
getline(fileIn,line,'*');
vCourse[i]._iDuration = line;
getline(fileIn,line,'*');
then you can assign to the struct.
Also if you want to do this
cout<<vCourse[i];
you will need to overload the operator<<
If you are unable to open your file, you need to check that you have 1) spelled the filename correctly and 2) that the file is in the same location as your executable. Probably would be safer to write the full pathname anyway

You can also try to put the content of file into single string and use strtok() function.

Related

how to discard from streams? .ignore() doesnt work for this purpose, any other methods?

I have a lack of understanding about streams. The idea is, to read a file to the ifstream and then working with it. Extract Data from the stream to a string, and discard the part which is now in a string from the stream. Is that possible? Or how to handle those problems?
The following method, is for inserting a file which is properly read by the ifstream. (its a text file, containing informations about "Lost" episodes, its an episodeguide. It works fine, for one element of the class episodes. Every time i instantiate a episode file, i want to check the stream of that file, discard the informations about one episode (its indicated by "****", then the next episode starts) and process the informations discarded in a string. If I create a new object of Episode I want to discard the next informations about the episodes after "****" to the next "****" and so on.
void Episode::read(ifstream& in) {
string contents((istreambuf_iterator<char>(in)), istreambuf_iterator<char>());
size_t episodeEndPos = contents.find("****");
if ( episodeEndPos == -1) {
in.ignore(numeric_limits<char>::max());
in.clear(), in.sync();
fullContent = contents;
}
else { // empty stream for next episode
in.ignore(episodeEndPos + 4);
fullContent = contents.substr(0, episodeEndPos);
}
// fill attributes
setNrHelper();
setTitelHelper();
setFlashbackHelper();
setDescriptionHelper();
}
I tried it with inFile >> words (to read the words, this is a way to get the words out of the stream) another way i was thinking about is, to use .ignore (to ignore an amount of characters in the stream). But that doesnt work as intended. Sorry for my bad english, hopefully its clear what i want to do.
If your goal is at each call of Read() to read the next episode and advance in the file, then the trick is to to use tellg() and seekg() to bookmark the position and update it:
void Episode::Read(ifstream& in) {
streampos pos = in.tellg(); // backup current position
string fullContent;
string contents((istreambuf_iterator<char>(in)), istreambuf_iterator<char>());
size_t episodeEndPos = contents.find("****");
if (episodeEndPos == -1) {
in.ignore(numeric_limits<char>::max());
in.clear(), in.sync();
fullContent = contents;
}
else { // empty stream for next episode
fullContent = contents.substr(0, episodeEndPos);
in.seekg(pos + streamoff(episodeEndPos + 4)); // position file at next episode
}
}
In this way, you can call several time your function, every call reading the next episode.
However, please note that your approach is not optimised. When you construct your contents string from a stream iterator, you load the full rest of the file in the memory, starting at the current position in the stream. So here you keep on reading and reading again big subparts of the file.
Edit: streamlined version adapted to your format
You just need to read the line, check if it's not a separator line and concatenate...
void Episode::Read(ifstream& in) {
string line;
string fullContent;
while (getline(in, line) && line !="****") {
fullContent += line + "\n";
}
cout << "DATENSATZ: " << fullContent << endl; // just to verify content
// fill attributes
//...
}
The code you got reads the entire stream in one go just to use some part of the read text to initialize an object. Imagining a gigantic file that is almost certainly a bad idea. The easier approach is to just read until the end marker is found. In an ideal world, the end marker is easily found. Based on comments it seems to be on a line of its own which would make it quite easy:
void Episode::read(std::istream& in) {
std::string text;
for (std::string line; in >> line && line != "****"; ) {
text += line + "\n";
}
fullContent = text;
}
If the separate isn't on a line of its own, you could use code like this instead:
void Episode::read(std::istream& in) {
std::string text;
for (std::istreambuf_iterator<char> it(in), end; it != end; ++it) {
text.push_back(*it);
if (*it == '*' && 4u <= text.size() && text.substr(text.size() - 4) == "****") {
break;
}
if (4u <= text.size() && text.substr(text.size() - 4u) == "****") {
text.resize(text.size() - 4u);
}
fullContent = text;
}
Both of these approaches would simple read the file from start to end and consume the characters to be extracted in the process, stopping as soon as reading of one record is done.

takin input from a file and saving it into temporary memory?

I have a structured array that is initially empty and has 4 data types withing, string, 2 int's and 1 float. I have a list of dvd titles and other attributes (3 other,2 of them are int the last one is float) saved in a text file and I need to input the data from the text file to my structure. this is my code, but seems like it isnt working?
do
{
for(int i=0;i<MAX_BOOKS;i++)
{
tempTitle= getline(myfile,line);
temChapters = getline(myfile,line);
tempReview = getline(myfile,line);
tempPrice = getline(myfile,line);
}
}while(!myfile.eof());
The return from getline is the stream you read from, not the string you read data into.
You're also repeatedly reading data into the same place (line), without saving it anywhere.
Your loop is defective (while (!somefile.eof()) is essentially always broken).
What you typically want is to start by overloading operator>> to read a single logical item from a stream, then use that to fill a vector of those items.
// The structure of a single item:
struct item {
std::string title;
int chapters;
int review;
int price;
};
// read one item from a stream:
std::istream &operator>>(std::istream &is, item &i) {
std::getline(is, i.title);
is >> i.chapters >> i.review >> i.price;
is.ignore(4096, '\n'); // ignore through end of line.
return is;
}
// create a vector of items from a stream of items:
std::vector<item> items((std::istream_iterator<item>(myfile)),
std::istream_iterator<item>());
You should structure it like this:
std::ifstream myfile("filename.dat");
std::string line;
while (getline(myfile, line))
{
// you can use std::string::find(), std::string::at(), std::string::substr
// to split the line into the necessary pieces
}
You can then use Jerry coffins answer to save each piece in a vector<item>.

How to find specific string constant in line and copy the following

I am creating a somewhat weak/vague database (My experience is very little, and please forgive the mess of my code). For this, I create a check everytime my console program starts that checks whether a database (copied to userlist.txt) is created already, if not a new will be created, if the database exists, however, it should all be copied to a 'vector users' (Which is a struct) I have within the class 'userbase' that will then contain all user information.
My userstats struct looks like this,
enum securityLevel {user, moderator, admin};
struct userstats
{
string ID;
string name;
string password;
securityLevel secLev;
};
I will contain all this information from a textfile in this code,
int main()
{
Userbase userbase; // Class to contain userinformation during runtime.
ifstream inFile;
inFile.open("userlist.txt");
if(inFile.good())
{
// ADD DATE OF MODIFICATION
cout << "USERLIST FOUND, READING USERS.\n";
userstats tempBuffer;
int userCount = -1;
int overCount = 0;
while(!inFile.eof())
{
string buffer;
getline(inFile, buffer);
if (buffer == "ID:")
{
userCount++;
if (userCount > overCount)
{
userbase.users.push_back(tempBuffer);
overCount++;
}
tempBuffer.ID = buffer;
cout << "ID"; // Just to see if works
}
else if (buffer == "name:")
{
cout << "name"; // Just to see if works
tempBuffer.name = buffer;
}
else if (buffer == "password:")
{
cout << "password"; // Just to see if works
tempBuffer.password = buffer;
}
}
if (userCount == 0)
{
userbase.users.push_back(tempBuffer);
}
inFile.close();
}
...
What I try to do is to read and analyze every line of the text file. An example of the userlist.txt could be,
created: Sun Apr 15 22:19:44 2012
mod_date: Sun Apr 15 22:19:44 2012
ID:1d
name:admin
password:Admin1
security level:2
(I am aware I do not read "security level" into the program yet)
EDIT: There could also be more users simply following the "security level:x"-line of the preceding user in the list.
Now, if the program reads the line "ID:1d" it should then copy this into the struct and finally I will put it all into the vector userbase.users[i]. This does not seem to work, however. It does not seem to catch on to any of the if-statements. I've gotten this sort of program to work before, so I am very confused what I am doing wrong. I could really use some help with this. Any other kind of criticism of the code is very welcome.
Regards,
Mikkel
None of the if (buffer == ...) will ever be true as the lines always contain the value of the attribute contained on each line as well as the type of the attribute. For example:
ID:1d
when getline() reads this buffer will contain ID:1d so:
if (buffer == "ID:")
will be false. Use string.find() instead:
if (0 == buffer.find("ID:")) // Comparing to zero ensures that the line
{ // starts with "ID:".
// Avoid including the attribute type
// in the value.
tempBuffer.ID.assign(buffer.begin() + 3, buffer.end());
}
As commented by jrok, the while for reading the file is incorrect as no check is made immediately after getline(). Change to:
string buffer;
while(getline(inFile, buffer))
{
...

Problems when reading from the computer memory, information previously got from a file

The problem that I have is that I have writen a code that has to get information from the computer memory. This information has previously got from a ".txt" file and stored in the computer memory in a particular way "simulating a database", but when accessing to the information I have a problem because the code doesn't run in the way I want. Have a look at the code and you will understand what I mean easily.
I enclose a example of ".txt" file that I use and the code I have writen.
Table.txt
1|1|30
0|20713TRVl\|8304pCXx|10600UeVt|30935giNrIs|4295D_GvCZM|9311aXuA|30063iTE`G|22658p``PwVJ|18362ZcCE|22644Y]OH|17221UG[]]nX|17084xvEcJVA|3657NP\sg|28737qMZNFm`|895Xupv|16773wASvjpe|5681zqniXpS|10873dwRLmCi|22173KSc`|18129Ekxa|16735rsqT`h|15514pCIB_oE|24295hFk_h|7752\PZX|2233wWOO|29169Y]sFDGx|669[u\M|11533EU]L|25080jaLU
1|19710WM``|11558iiGJg|16155ieHOV|28466QLoVB|18135ygKI|17776VmzB_tj|6481iUwYmN|17666_RDKvt|31467^LIMj|22316YWzc|12797YgoH|6353awg]|27166\FdVg|20077BrEzm|5447qeLC_EH|9193hhQ[l_B|17844WZW`fMe|29811STcAQpO|28993\pLiCh|4987VRSZ]q|12728yCSy]w[|13678ZRPx|5236yMl_|27648Fo_PTXb|6822iznH|13276jQCw|20282daHHmrb|26134eDWqr]u|12310eKt[NZC
2|27807qgNgei|11243Jr_QNtb|24086MzGN]f|9987tugrZj^|9760XEtQVF|28587yY\SH|4842RX`d|3041J]xQgm|24409adRD[U|18525]`nO|24535HyalLG|18439Ilqd|23040tqvfj|8036ROxWCT|25261h[O_|25226eSfjQJ|21524mSJYF|2528[ztVA|31877OznR|27727x_ck_|24510VFKV^xQ|30384MPPOTG|13334nF^xvTM|19217dOsn|2222RQCxC|23913F[vkSsJ|23733uzQQsJc|29996_MMfiRl|10372dYZSwjD
3|31258oSce|7118Vzz\|19941YMRaGx[|18653sMCN|1849klofOhd|14780zuoLZk|19426Ptacg|6885KL^]E|16484Sv]F|29183NEIv|26521aSoDDXU|5456iJqSU|30851qgpz]Tr|22097plFFPFi|12998mL^jt|3993wejdD|8889QUiKc|12706oZd_DXD|3512^EBc]|5691[xXE|14064IfJa|20823jGOxpQ|15712EfWHA|15008cPHve\|15683wrVb|5808IMQcBG|25292xVmOVDp|31242eiTvZF|30793qWgntqf
4|19444dLHkrxk|10456FFb^V|21960sGZfse\|19715xquh|32009RfFmA|28303_QygwKx|28444McTs|5352vQFB|17659uofx|26969QEnBd|22372KVXl|30644KatEOd|8183EsFNBNs|26162P^bxe_E|24586eveWZHE|24907JHRLZdg|6834qctb|11977tOXuXB|12554HUX^F|7215_ZahLAa|3589VjHFc|17681PA]vTqb|16490CBTQ|10866Y^Dno\|26235R]srCEF|13897TVdx`J|27500pcigdWb|11664PYsc|4020HgTsBYq
5|26342wCyV|28887CJ\[fPs|16881^pRVuYY|12388JeriY|2979YQ\w|8099qK\]X|29998]eu_`O|27700NYuL|289bMWVe|4607VpJrmKs|18336koiQ|8611vQrxdYW|6912ttnxpRU|2241CENyF[|29767_mEfEN|14521EiHkHg|22504SBwFbz|29515jYCrme|5754GkzrHAw|14375IiIx|1059QxDB|8136L\gt|18302KScaXq|19659FRYB|16752NHox|23132`izp|23499bgjoprd|30650WVxHkz|1676QqKUJf
6|15294oIFZPM|5488EPgR|24406Mmy^|20820OCMt^q|16097][kmq|29440gR^Lvy|26456OEPdPz|27616[tfnXC|13216IKIYAeG|24027fnDA|17957kiPq|28187ySUmCAf|17381OgaQx|15971vzKkk`|3135UTgf|25648iInf|24565QZA_|9200sp[hi[|18362N`cEpqt|2804rmKJ|11554D[WS|5662O]QK`|13908wbICUok|20744Ixlif|29609STKr|23818Cs`Qp_c|20962dkgnf|8591^ReW\E|28911WtMeO[
7|18239uWQFFF|6201`yH]B|5791]L[v|18435jHkOvT|25511^ySzrH[|18056MjE_JF|14876ipSiEP|29780c`YpTuS|26264pnRADf|12127wIQsq|25181Cdxxq]Z|16977AtaIsv|27087^p^B|1344dnNWLv|9555JOkNp|31734WOOn|18917rCrm\j|28826es_O]f|1811A`mJx|19198naaTi|20803c[StHg]|18017U]jt[z|17751vROZwnS|19350tt]x`G|23177NyJuqd|29149auTX|31230WiRcX|13431uedgr[l|6252Zhkxmed
8|19669Ettu^S|1313RLnFQg|19265jGVvs|9997ZvsCgGB|21710Tsdg\x|32553RgvPH|12229xjWYIe|8512^IWkNSm|28498BXIsolk|2299KxvZ|16969yoUUVR|5934uu`K|22919^ZOjVRW|25677_csd]bx|14661wbz_|10185QrmzV[|13051lk_kx|26726[kipm|14861BPuPR|13763dtsLp|11680zhor|26464ZqZFb[S|27281bnAZx|30023QphlfNq|32382_wnOWR|6505VhoajP|23526hhycvC[|22126tjbx^\|12085FLtac
9|31896e]mX|11280S[Okg|31451JpeC|13201rSPJsr|28026pLxYgW|1228Ma[O|21756IKkYJFn|21814BYyyhIk|32054VN`j|22057VqEm|13823pdQphy|27089S]utdK|28215TUDMDnv|26276D^\OQ|12959rxlK|688ThznW|9471GiWCPT|26023sNyOjm|18837sOBDM_m|19889pbzFYO|30767XxOCoAE|10377F\bLdp|11542uXXfPj|16480w[UA[x|31384M]s]c|14296E[Iv|31259tUFZl|11865Aj_ib|22757wVYILE
And here it is my code
#include "hybridDDBBHeader.h" // This is a .h with different classes defined by me
// We have a class "HybridDataBase" that contains the attributes vector<Table> pointer
// We have a class "Table" that contains the attributes int id, int type, int columns, and vector<ElementSet> RowsCols
// We have a class "ElementSet" that contains the attributes vector<strings> elements
int main()
{
vector<Table> TablesDescriptor;
ifstring ifs("C:\\Table\\Table.txt");
int id;
int type; // Don't worry about the meaning of this variable
int columns; // Don't worry about the meaning of this variable
vector<string> buffer;
vector<ElementSet> RowsCols;
string temp; // Variable where the first row will be stored
getline( ifs, temp ); // We get ONLY the FIRST LINE of my ".txt" file and we work with it. We take it from my file ifs and we store this first line in string temp
size_t tokenPos = temp.find("|"); // When we find the simbol "|" we will identify different elements
int i=0;
while (tokenPos != string::npos)
{
tokenPos = temp.find("|");
if(i==0)
{
id = atoi(temp.substr(0, tokenPos).c_str());
}
if(i==1)
{
type = atoi(temp.substr(0, tokenPos).c_str());
}
if(i==2)
{
columns = atoi(temp.substr(0, tokenPos).c_str());
}
temp.erase(0, tokenPos+1);
i=++i;
}
// We continue reading the NEXT LINES of our code UNTIL THE END of the file
while(getline( ifs, temp ))
{
size_t tokenPosition = temp.find("|"); // When we find the simbol "|" we will identify different elements
while (tokenPosition != string::npos)
{
string element;
tokenPosition = temp.find("|");
element = temp.substr(0, tokenPosition);
buffer.push_back(element);
temp.erase(0, tokenPosition+1);
}
ElementSet ss(0,buffer);
RowsCols.push_back(ss);
}
Table TablesStorage (id, type, columns, RowsCols); // After every loop we will store the information about every .txt file in the vector<Table> TablesDescriptor
TablesDescriptor.push_back(TablesStorage); // In the vector<Table> TablesDescriptor will be stores all the different tables with all its information
}
HybridDataBase hybriddatabase(1, TablesDescriptor);
cout<<"--- SOLUTION ---"<<endl;
ElementSet selectedElementSet;
Table selectedTable = hybriddatabase.getPointer().at(0); // This works perfectly
selectedTable.setType(1); // This works perfectly, but don't worry about this
selectedElementSet=selectedTable.getRowsCols().at(3); // PROBLEEEEEEM!!!!!!!!!!!! It doesn't read information from row 3, always row 0
cout<<"selectedElementSet: "<<selectedElementSet.getElements().at(0)<<endl; // This works perfectly
I will be waiting for your help. Thank you very much in advance!!! :)
After ElementSet ss(0,buffer); you probably want to do buffer.clear();
Also, this is pointless: i=++i;. Use ++i;

getline and file handling

I want to read the first lines of 2 separate files and then compare them...the following is the code i use but it gives me "istream to string error". do i need to use a while condition to start reading the files first?
ifstream data_real(filename.c_str()); /*input streams to check if the flight info
are the same*/
ifstream data_test("output_check.txt");
string read1, read2;
string first_line_input = getline(is,read1);
string first_line_output_test = getline(data_test,read2);
string test_string1, test_string2;
int num_lines_output_test, num_lines_input;
if((first_line_input.substr(0,3)==first_line_output_test.substr(0,3)))
{
while(!data_test.eof()) // count the number of lines for the output test file with the first flight info
{
getline(data_test,test_string1);
num_lines_output_test++;
}
while(getline(is,test_string2)) // count the number of lines for the output test file with the first flight info
{
if(test_string2.substr(0,3)!="ACM")
num_lines_input++;
else
break;
}
}
getline(istream, string) returns a reference to the istream, not a string.
So, comparing the first line of each file could be something like:
string read1, read2;
if !(getline(is,read1) && getline(data_test,read2)){
// Reading failed
// TODO: Handle and/or report error
}
else{
if(read1.substr(0,3) == read2.substr(0,3)){
//...
Also: Never use eof() as a termination condition for a stream reading loop. The idiomatic way to write it is:
while(getline(data_test,test_string1)) // count the number of lines for the output test file with the first flight info
{
num_lines_output_test++;
}
Try adding this helper function:
std::string next_line(std::istream& is) {
std::string result;
if (!std::getline(is, result)) {
throw std::ios::failure("Failed to read a required line");
}
return result;
}
Now you can use lines from the file the way you want (i.e. to initialize strings, rather than modify them):
string first_line_input = next_line(is);
string first_line_output_test = next_line(data_test);