C++ reading a file into a struct - c++

Using fstreams I have a file opened that contains numerous lines. Each contiguos set of 4 lines are such that: the first line is an int, the second and third are strings and fourth is a double. This sequence continues till EOF.
I'm attempting to load these lines into a struct array:
struct Library {
int id;
string title;
string artist;
double price;
};
and the code I'm trying to implement to load data into the struct is this:
const int LIMIT = 10
Library database[LIMIT];
ifstream file;
file.open("list.txt");
if(file) {
while(!(file.eof()) && counter < LIMIT) {
file >> database[counter].id;
getline(file, database[counter].title;
getline(file, database[counter].artist;
file >> database[counter].price;
}
} else {
...
}
// Using the following to debug output
for(int i = 0; i < counter; i++) {
cout << "ID: " << database[i].id << endl
<< "Title: " << database[i].title << endl
<< "Artist: " << database[i].artist << endl
<< "Price: " << database[i].price << endl
<< "-----------------------" << endl;
}
The file I'm trying to throw at this thing is
1234
Never Gonna Give You Up
Rick Astley
4.5
42
Thriller
Michael Jackson
32.1
The problem I'm having here is that between reading the id and title using file >> ... and getline(...) is that somewhere a newline bite is being introduced screwing up the output, which displays this monstrosity...
ID: 1234
Title:
Artist: Never Gonna Give You Up
Price: 0
--------------------
ID: 0
Title:
Artist:
Price: 0
--------------------
The solution is probably the most basic of solutions, but mainly because I can't figure out exactly what is going on with the newline bite I can't combobulate a phrase to shove into google and do my stuff there, and I'm at the stage where I've been looking at a problem so long, basic knowledge isn't working properly - such as how to handle basic input streams.
Any form of help would be much appreciated! Thanks in advance :)

This happens because the >> operator for the input stream only grabs part of a line, and does not always grab the newline character at the end of the line. When followed by a call to getline, the getline will grab the rest of the line previously parsed, not the line after it. There are a few ways to solve this: you can clear the buffer from the input stream after each read, or you can simply get all your input from getline and just parse the resulting strings into an integer or a double when you need to with calls to stoi or stod.
As a side note, you don't want to detect the end of your file the way you presently are. See why is eof considered wrong inside a loop condition?

You can solve this problem by adding:
fflush(file);
everytime before you use getline(file, ...). Basically this will clear the input buffer before you use the getline() function. And fflush() is declared in the cstdio library.

file >> database[counter].id;
will read, in this case, a whitespace separated sequence of characters that is interpreted as an int. The newline is considered whitespace. You should now be sitting on that newline character, thus the getline() will read nothing -- successfully -- and increment the file position just past that.
You may be better off using getline() for each line and then separately interpreting the lines from the reading. For example, the first line read could be interpreted with a subsequent std::stoi() to get the integer representation from the string.

Related

why I cant read a file to an integer vector?

well! I have a text file including some integer values and non-integers like character strings and white spaces so I want only to read integers values so I used a vector of integers but when I read the file the opining is ok but it seems the first input fails thus breaks the loop!!!
here is my main example:
ifstream in("file.txt");
if(in.fail())
cout << "opening failed!" << endl;
//opening is fine!
int value;
vector<int> v;
while(in >> value) // the problem here; it fails why?
{
cout << "ok"; // not printed
v.push_back(value);
}
cout << v.size() << endl; // 0??!!
this is the content of file.txt:
32 43 24 32
15 23
57
77 81
if I make a vector of chars it's ok but I want only to use one of integers
*** I already used a code like this and worked fine but now I don't know what happened??!!! it's really annoting
any help, comment, tip is welcome and appreciated
This line:
while(in >> value)
says while I can read integers...
But in the post this may not be true - you are not handling this case.
Either read stuff that is not integers and handle it. Or just read strings and then decide what to do.
In addition
cout << "ok"; // not printed
is because it is buffered.
Do this
cout << "ok" << flush; // printed
excuse me first for annoying you with nonsense question. finally I managed to discover the error:
in my main folder of project I unintentionally created a winrar file input.rar then I didn't remove it but rename it to input.txt it's ok I opened it manually and removed some unreadable characters. then I put inside it the content above of integers then my c++ application succeeds in opening it but can't read it.
*now I removed it input.txt which was input.rar and created a new document text input.txt and now everything is good!!!
thank you for your collaboration. and this post may help someone else.
* don't create rar file or other formats then rename them to be text files and try to read them via your c++ fstream because it'll fail in fact it'll produce an error-prone which looks impossible to solve

How to extract specific substring from getline function in C++?

I'm fairly new to C++ so please forgive me if my terminology or methodology isn't correct.
I'm trying to write a simple program that:
Opens two input files ("infileicd" and "infilesel").
Opens a single output file "list.txt".
Compares "infilesel" to "infileicd" line by line.
If a line from "infilesel" is found in "infileicd", it writes that line from "infileicd" to "list.txt", effectively making a separate log file.
I am using the getline() function to do this but have run into trouble when trying to compare each file line. I think it might be easier if I could use only the substring of interest to use as a comparison.
The problem is that there are multiple words within the entire getline string and I am only really interested in the second one. Here are two examples:
"1529 nic1_mau_op_mode_3 "8664afm007-01" "1" OUTPUT 1 0 LOGICAL 4 4136"
"1523 pilot_mfd_only_sel "8664afm003-02" "1" OUTPUT 1 0 LOGICAL 4 4112"
"nic1_mau_op_mode_3" and "pilot_mfd_only_sel" are the only substrings of interest.
It would make it a lot easier if I could only use that second substring to compare but I don't know how to extract it specifically from the getline() function. I haven't found anything suggesting it is impossible to do this, but if it is impossible, what would be an alternative method for extracting that substring?
This is a personal project so I'm under no time contstraints.
Any assistance is greatly apprecated in advance. Here is my code (so far):
int main()
{
//Open the file to write the selected variables to.
ofstream writer("list.txt");
//Open the selected variabels file to be read.
ifstream infilesel;
infilesel.open("varsel.txt");
//Open the icd file to be read.
ifstream infileicd;
infileicd.open("aic_fdk_host.txt");
//Check icd file for errors.
if (infileicd.fail()){
cerr << "Error opening icd.\n" << endl;
return 1;
}
else {
cout << "The icd file has been opened.\n";
}
//Check selected variables file for errors.
if (infilesel.fail()){
cerr << "Error opening selection file.\n" << endl;
return 1;
}
else {
cout << "The selection file has been opened.\n";
}
//Read each infile and copy contents of icd file to the list file.
string namesel;
string nameicd;
while(!infileicd.eof()){
getline(infileicd, nameicd);
getline(infilesel, namesel);
if (nameicd != namesel){ //This is where I would like to extract and compare the two specific strings
infileicd; //Skip to next line if not the same
} else {
writer << nameicd << namesel << endl;
}
}
writer.close();
infilesel.close();
infileicd.close();
return 0;
}
So, based on what we discussed in the comments, you just need to toss the stuff you don't want. So try this:
string namesel;
string nameicd;
string junk;
while(!infileicd.eof()){
// Get the first section, which we'll ignore
getline(infileicd, junk, ' ');
getline(infilesel, junk, ' ');
// Get the real data
getline(infileicd, nameicd, ' ');
getline(infilesel, namesel, ' ');
// Get the rest of the line, which we'll ignore
getline(infileicd, junk);
getline(infilesel, junk);
Basically, getline takes a delimiter, which by default is a newline. By setting it as a space the first time, you get rid of the first junk section, using the same method, you get the part you want, and then the final portion goes to the end of the line, also ignoring it.

How to move through each line in getline()

Whenever I run my code, I get the first line pulled out of the file, but only the first. Is there something I am missing? I ran into the issue when I implemented stringstream to try and more easily read in the lines of hex from the file and more quickly convert between a string to a hex value. It read in each line accordingly before, but now it is not. Am I missing something in the understanding of how getline() works?
ss is stringstream, fileIn is the file, hexInput is a string, memory[] is an array of short int, instruction is a short int, opCounter is an int...
string hexInput;
stringstream ss;
short int instruction;
ifstream fileIn ("proj1.txt");
if (fileIn.is_open())
{
while ( getline(fileIn, hexInput) )
{
ss << hex << hexInput;
ss >> instruction;
memory[opCounter] = instruction;
cout << hex << memory[opCounter] << '\t';
cout << opCounter << '\n';
ss.str("");
opCounter++;
}
fileIn.close();
}
else cout << "Unable to open file";
Above is the entire function (which was working before using stringstream) and below are the contents of the file.
4000
0033
0132
2033
4321
2137
D036
A00F
B003
C00C
3217
6217
E044
FFFF
6016
1013
FFFF
0
0
0
0
1
2
3
4
5
There are 26 lines and the last opCounter output says "19" in hex which makes me assume the file is being read line-by-line, but the stringstream never updated. This is my first C++ program and am new to a few of these features I am trying to implement.
Thanks for any help...
Your stringstream is created correctly from the first line. After outputting the number into instruction it will be eof though (you can check this with ss.eof()) because there is no data after the first number inside the stringstream.
replace ss.str(""); (which you don't need) by ss.clear(); which will reset the eof flag. Inputting the new line and reading from the stream will then work as expected.
Of course there is absolutely no need for a stringstream in the first place.
while ( fileIn.good() ) {
fileIn >> hex >> instruction;
[...]
}
works fine. It will read short ints in hexadecimal representation until one line cannot be interpreted as such. (Which incidentally is line 7 because D036 is too large to fit inside a short int - admittedly that is different from your current behaviour, but did you really want a silent failure? very useful at this point are again fileIn.eof() to check whether the read failed due to the stream being at the end of the file and fileIn.clear() to reset other fail-bits)
As requested:
The loop is commonly abbreviated as
while ( fileIn >> hex >> instruction ) {
[...]
}
but note, that if you want to check why the read failed, and continue if it was not an eof, the aforementioned loop is more suited to the task.

C++ cout overwriting itself while in for loop

The cout statement in this for loop:
for (vector<Student>::iterator qw = students.begin(); qw != students.end(); ++qw){
Student a = *qw;
name = a.getName();
regno = a.getRegNo();
std::cout << "Name: "<< name << " Reg Number: " << regno << endl;
}
Is creating some odd behavior, what the cout should print is something like this:
Name: Mike Sanderson Reg Number: 10101
However which it actually prints out it:
Reg Number: 10101on
It would seem to me that after the second part of the cout statement it is going back to the start of the line and overwriting itself, but why?
Hope you guys can help me and if you need more info let me know!
This is what the carriage return character does (that is, \r in a string literal). I assume name string has an \r at the end of it. You'll need to figure out how it got there and remove it.
I'm guessing that perhaps you read the names from a file, and that file was created on Windows, which ends lines with \r\n by default. C++ will usually handle the conversion between line endings for you when reading from a text file, but if you're reading the file as a binary file and using \n as a delimiter, you'll have this problem. The \r will be read as though it were part of the line.

Reading a ";" delimited file into a character array structure

I am trying to read a set of values from a text file into an array of structures of arrays. The entries are each separated by a '\n', and each entry consists of 3 values, separated by a ';'.
The problem is that after correctly reading the first line of file data the program reads the first value from the second line, then seems to fail to read the remaining values. Can you point out the error in my syntax or logic?
The test data appears below.
CS162;Finish Lab 2;9/26/2009
CS201;Take Quiz 1;9/28/2009
After reading in the test data my program's output is below.
Your tasks are:
Finish Lab 2 for CS162 is due 9/26/2009
CS201
for is due
The loops that read the file into the array and outputs the array contents are below. My complete code will be at the end of the question.
for ( ; InputFile.peek() != EOF; ListSize++ )
{
InputFile.get(TaskList[ListSize].Course, BUFFERSIZE, ';');
InputFile.ignore(BUFFERSIZE, ';');
InputFile.get(TaskList[ListSize].Assignment, BUFFERSIZE, ';');
InputFile.ignore(BUFFERSIZE, ';');
InputFile.get(TaskList[ListSize].DueDate, BUFFERSIZE, ';');
InputFile.ignore(BUFFERSIZE, '\n');
}
cout << "Your tasks are:" << endl;
for ( int Iteration = 0; Iteration <= ListSize; Iteration++ )
{
cout << TaskList[Iteration].Assignment << " for " << TaskList[Iteration].Course << " is due " << TaskList[Iteration].DueDate << endl;
}
Full disclosure, this is for a computer science class. This is why I am not asking for complete code solutions, just help with logic or syntax errors. If I am doing this in completely the wrong way, please point me to documentation to help me. But this does put limitations on my code. The program must use character arrays, not strings.
Perhaps the last get should be:
InputFile.get(TaskList[ListSize].DueDate, BUFFERSIZE, '\n');
instead of
InputFile.get(TaskList[ListSize].DueDate, BUFFERSIZE, ';');
Your last field (due date) does not have a semicolon at the end, only a newline.
Update: I suggest you also look into using getline instead of get. They have similar functionality, but getline will consume the delimiter also, meaning that you won't need to use the ignore().
Without thinking about the code you have written, I'll just say that my normal pattern for this type of problem is:
while (readline) { processline; }
Incremental file processing is more likely to run into problems if you don't have everything exactly correct.