getline with an input file using a function, array and structure - c++

I am writing a program for my class that takes a text file and has each line in the text assigned.The problem i am having is that when i use the getline() its completely skipping the input. From everything i have read i feel like i am doing it right but obviously i am missing something.This is a small part of the code...if you need the whole thing i can post it.Thanks ahead of time for everyone's help.
garage populategarage()
{
garage tg; //temporary garage
string instruction_prompt;
initialize_garage(tg);
ifstream inputFile;
inputFile.open("garage-input-file.txt");
for (int i = 0; i < NUM_OF_VEHICLES; i++)
{
inputFile >> tg.vehicles[i].category;
if (tg.vehicles[i].category == "\0")
i = NUM_OF_VEHICLES;
else
{
inputFile >> tg.vehicles[i].make;
inputFile >> tg.vehicles[i].model;
inputFile >> tg.vehicles[i].color;
inputFile >> tg.vehicles[i].year;
inputFile >> tg.vehicles[i].trans;
inputFile >> tg.vehicles[i].cylinder;
getline(inputFile, tg.vehicles[i].drive); //This is my problem
}
}

You haven't shown any example input, or what your results are, but from your description it seems like each value appears on its own line. Would that be right? In that case, the getline call is likely to be picking up the previous newline (from when you read cylinder), and returning an empty string.
If your values appear on separate lines, you should probably use getline for each. You can wrap this behaviour for any type into a template function - something simple like:
template <class T>
istream & readValue( istream & s, T & value )
{
string line;
if( getline( s, line ) )
{
istringstream iss( line );
iss >> value;
}
return s;
}
If instead your file contains a single line for each entry, then it's usually better to use getline for the whole line, then read individual values from an istringstream.
for (int i = 0; i < NUM_OF_VEHICLES; i++)
{
string line;
if( !getline( inputFile, line ) ) break;
istringstream iss( line );
iss >> tg.vehicles[i].make;
iss >> tg.vehicles[i].model;
// etc...
}

Related

Reading in from a CSV data file, erroring out when trying to pull in only what I need

I am trying to pull two columns of data from a CSV file and dump the rest.
The errors I am receiving are:
C2296: '>>': illegal, left operand has type 'std::basic_istream> &(__thiscall std::basic_istream>::* )(_Elem *,std::streamsize)'
C3867: 'std::basic_istream>::read': non-standard syntax; use '&' to create a pointer to member
The data is formatted as such:
1928,44.50%,.......
I want the 1928 assigned into data.year, and the 44.50% assigned into data.yield, but not including the percent sign.
bool ReadData(MyData &data)
{
//Variable for reading data into file stream
ifstream inFile;
string trash;
char junk;
cout << "\nReading file . . .";
//Open data file
inFile.open("Data.csv");
//Read the first 18 lines, and throw it away
for (int i = 0; i < 18; ++i)
{
getline(inFile, trash);
}
//Read the necessary data into the arrays
for (int i = 0; i < SIZE; ++i)
{
//===============================================================
//This line is throwing 2 errors
//Goal: read first column of a simple integer into data.year, discard the comma, then read the second column of a double into data.yield, discard the percentage sign. infile.ignore(); to clear cin stream, getline(inFile, trash) to discard remainder of the lines.
inFile.read >> data.year[i] >> junk >> data.yield[i] >> junk >> trash >> endl;
//===============================================================
inFile.ignore();
getline(inFile, trash);
}
//Return false if file could not be opened
if (!inFile)
{
cout << "\n\nTechnical error! The file could not be read.";
return false;
}
else
{
cout << "\n\nFile opened successfully!";
return true;
}
inFile.close();
}
struct MyData
{
int year[SIZE];
int yield[SIZE];
double minYield;
double maxYield;
double avgYield;
};
Where am I going wrong?
The very first problem is reading a file line by line constant number of times, however you never know the size of the file. So, you should add another check to your for loop. The second problem is that you say the yield is an int but it is a double in the file. The third problem is reading formatted data is not something like you did. The following piece of code can work for you, or you can play a bit with the code.
for (int i = 0; i < SIZE && std::getline(infile, line); ++i) {
std::stringstream linestream(line);
std::string year, yield;
getline(linestream,year,',');
getline(linestream,yield,',');
yield.erase(std::remove(yield.begin(), yield.end(), '%'), yield.end()); // remove %
myData.year[i] = std::stoi( year ); // string to int
myData.yield[i] = std::stod( year ); // string to double
}
PS: Don't forget to include sstream library.
inFile.read >> data.year[i] >> junk >> data.yield[i] >> junk >> trash >> endl;
inFile.read is a function and has no operator >>, that's why you get the error. See https://en.cppreference.com/w/cpp/io/basic_istream/read
I'd suggest you try a different approach: Read the entire line and use a explode function to retrieve the individual elements. For example Is there an equivalent in C++ of PHP's explode() function?

Reading in from file gives unexpected output

I am working on reading in from a file and parsing through data from command line argument for homework. And I ran in a wall and I do not know what's the problem, and I hope I could get some advice on what I am missing.
The data file is composed thusly; on the first line, it has number of total lines. For each line after that, it is a line of string separated by | character. I need the '|' character because I want to split my string into substrings.
Here is an example of input file.
3
league of legends|Teemo|Master Yi|Vayne
apple|samsung|smart phone|smart watch
overwatch|d.va|junkrat|Reinhart
Here is my code.
int main( int argc, char* const argv[] )
{
//change string to char* so I can check through each char to see if the
//thing I read in is '|' character.
String Data = (argv[1]);
ifstream fin (Data.c_str());
//check whether the file is open.
if ( !fin.is_open() )
{
cout << "Could not open file" << endl;
}
else
{
int dataLines;
char dataBuffer[100];
//The first integer I read in will be how many lines I will loop through
fin >> dataLines;
//ignore the new line character and do not include it in the count of
//dataLines.
fin.ignore();
//use noskipws so I can recognize whitespaces.
fin >> noskipws >> dataBuffer;
//TEST CODE: COMMENTED OUT FOR NOW.
//cout<<dataBuffer<<endl;
//loop for the number of lines
for(int i = 0; i < dataLines; i++)
{
fin.getline(dataBuffer, 100);
//print the buffer for checking
cout<<dataBuffer<<endl;
}
}
//close the file.
fin.close();
return 0;
}
The result is supposed to look like this.
league of legends|Teemo|Master Yi|Vayne
apple|samsung|smart phone|smart watch
overwatch|d.va|junkrat|Reinhart
The actual result looks like this
of legends|Teemo|Master Yi|Vayne
apple|samsung|smart phone|smart watch
overwatch|d.va|junkrat|Reinhart
The first word that I read in from buffer is gone. "league" is the one that is missing, and I tried to see what the problem is by inserting the test code at the location specified in my code. With the given test code, my output is
league
of legends|Teemo|Master Yi|Vayne
apple|samsung|smart phone|smart watch
overwatch|d.va|junkrat|Reinhart
So the problem is that between reading in the file with noskipws and the forloop that loops over dataLine. Before the forloop my buffer is league. Yet once I enter the loop it is passed that and goes straight to of.
What am I missing here? What could be a possible solution?
Main problem:
fin >> noskipws >> dataBuffer;
Does two things. 1. >> noskipws turns off automatically skipping whitespace, unnecessary because of how OP is reading the stream. 2. >> dataBuffer reads the first word from the stream, in this case consuming the word "league"
Solution: Don't do this.
Other problems:
fin.ignore();
will ignore exactly one character. But what if someone left a nigh-invisible space after the count? Instead use
fin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
to ensure the rest of the line is consumed in its entirity.
char dataBuffer[100];
Why make yourself suffer? Instead use
std::string dataBuffer;
Recommendation:
Use std::stringstream and std::getline to tokenize the lines on '|'
std::stringstream stream(databuffer);
std::string token;
while (std::getline(stream, token, '|')
{
std::cout << token << ',';
}
You do not need the following line:
fin >> noskipws >> dataBuffer;
Tested with g++ 4.8.3 2 on RHEL 7.1
Thanks User 4581301. It reads in the data correctly and split with '|' character. Now I can work on storing the data into classes.
for anyone who may have same problem, this is the fixed up version of code.
int main( int argc, char* const argv[] )
{
String Data = (argv[1]);
ifstream fin (Data.c_str());
if ( !fin.is_open() )
{
cout << "Could not open file" << endl;
}
else
{
int dataLines;
char dataBuffer[100];
fin >> dataLines;
fin.ignore();
for(int i = 0; i < dataLines; i++)
{
while(fin.getline(dataBuffer, 100, '|'))
{
cout<<dataBuffer<<endl;// check to see if it reads in correctly.
}
}
}
fin.close();
return 0;
}

C++ Reading a file line by line with delimiter and store data into variable

I am quite new to C++ and I have a txt file with data which looks something like this:
test:123:lock
qwerty:4321:unlock
asdf:12:lock
Is it possible for me to read the data line by line into a variable / array using ":" as the delimiter?
I tried doing something like:
while(!myfile.eof()) {
for (int i = 0; i < 3; i++) {
getline(myfile,UserN[i],':');
}
}
What I want to achieve is to store the data of the first line into the UserN[0], UserN[1], and UserN[2]. And when it start reading the second line, the data on the second line will replace the value in UserN[0], UserN[1], and UserN[2]. Thanks in advance!
Read the line first, then tokenize it with std::stringstream:
#include <sstream>
...
std::string line;
while(std::getline(myfile, line)) { // cache the line
std::istringstream tokenizer(line);
std::getline(tokenizer, UserN[0], ':'); // then get the tokens from it
std::getline(tokenizer, UserN[1], ':');
std::getline(tokenizer, UserN[2]); // last token: get the remainder
// of the line.
if(tokenizer) {
// success!
} else {
// There were fewer than two colons in the line
}
}
In essence, std::istringstream wraps a string in a stream interface -- the resulting stream behaves (roughly) like a file with the same contents as the string with which it was built. It is then possible to use >> or getline or anything else that you could use on files or std::cin or other input streams with it, and here we use it to take the string apart into the tokens you require.
You can do this simply with
ifstream myfile( "aFile.txt" );
// .. check whether the file is open: if( !myfile.is_oppen() ) error
for( string userN[3]
; getline( getline( getline( myfile >> ws, userN[0], ':' ), userN[1], ':' ), userN[2] ); )
{
// userN[0..2] is read correctly
}
or in a more elegant way, perhaps more suitable to Your requirements. I assume, that the second text is always a number and the third text is either 'lock' or 'unlock' or something else like an enum.
enum class LockState
{
lock, unlock
};
// -- reading a LockState
// please consider, that behind the text must follow a white space character (Space, LF, ..)
std::istream& operator>>(std::istream& in, LockState& s)
{
std::string word;
if( in >> word )
{
if( word == "lock" )
s = LockState::lock;
else if( word == "unlock" )
s = LockState::unlock;
else
in.setstate( std::ios_base::failbit );
}
return in;
}
struct Entry // change the name 'Entry' of the struct suitable for Your requirements
{
std::string someText;
int aNr;
LockState lockState;
};
// -- function to read an 'Entry'-object
std::istream& operator>>(std::istream& in, Entry& e)
{
char colon;
if( getline( in >> std::ws, e.someText, ':' ) >> e.aNr >> colon
&& colon != ':' )
in.setstate( std::ios_base::failbit );
else
in >> e.lockState;
return in;
}
and later in Your main-program
ifstream myfile( "aFile.txt" );
// .. check whether the file is open: if( !myfile.is_oppen() ) error
for( Entry e; myfile >> e; )
{
// use here the Entry-object 'e'
}
if( myfile.eof() )
cout << "Ok - You read the file till the end" << endl;
Avoid trouble here and use the split function from Boost:
#include <fstream>
#include <vector>
#include <string>
#include <boost/algorithm/string.hpp>
// ...
// Read file and throw exception on error.
std::ifstream infile;
infile.open(file_name);
std::string line;
while (std::getline(infile, line))
{
// Strip of the comments.
std::vector<std::string> strings;
boost::split(strings, line, boost::is_any_of(":"));
// You have now a vector of strings, which you can process...
}

C++ file IO getline not pulling a string

I have a file that is opened filled like this:
STRING
INT
INT
INT
filename.txt
STRING
INT
INT
INT
filename1.txt
etcetera
I have code that is supposed to read from the file, pull the string, the integers, and the file names. It is able to pull the string and the integers, but it won't pull the file name.
Here is the code:
while( !input.eof() )
{
string name;
int type = UNKNOWN;
int pages;
float ounces;
getline( input, name );
input >> type >> pages >> ounces;
getline(input, reviewFile); //reviewFile is a static string called in the header file
input.ignore(INT_MAX, '\n');
}
It should work if you put the ignore before the getline, to eat the newline after the ounces:
while( !input.eof() )
{
string name;
int type = UNKNOWN;
int pages;
float ounces;
getline( input, name );
input >> type >> pages >> ounces;
input.ignore(INT_MAX, '\n');
getline(input, reviewFile); //reviewFile is a static string called in the header file
}
The operator>> will read words, not taking end of line into account that much.
Therefore, I would write something like this (As Massa wrote in a comment):
input >> type >> pages >> ounces >> ws;
Also, please note that "eof" checks are suboptimal. Try to avoid them. Moreover, you do not check after the reads within the loop if there is anything more to read.

c++ parsing lines in file as streams

I want to parse a file which describes a set of data line by line. Each datum consists of 3 or four parameters: int int float (optional) string.
I opened file as ifstream inFile and used it in a while loop
while (inFile) {
string line;
getline(inFile,line);
istringstream iss(line);
char strInput[256];
iss >> strInput;
int i = atoi(strInput);
iss >> strInput;
int j = atoi(strInput);
iss >> strInput;
float k = atoi(strInput);
iss >> strInput;
cout << i << j << k << strInput << endl;*/
}
The problem is that the last parameter is optional, so I'll probably run into errors when it is not present. How can i check in advance how many parameters are given for each datum?
Furthermore,
string line;
getline(inFile,line);
istringstream iss(line);
seems a bit reduldant, how could I simplyfiy it?
Use the idiomatic approach in this situation, and it becomes much simpler:
for (std::string line; getline(inFile, line); ) {
std::istringstream iss(line);
int i;
int j;
float k;
if (!(iss >> i >> j)) {
//Failed to extract the required elements
//This is an error
}
if (!(iss >> k)) {
//Failed to extract the optional element
//This is not an error -- you just don't have a third parameter
}
}
By the way, atoi has some highly undesired ambiguity unless 0 is not a possible value for the string you're parsing. Since atoi returns 0 when it errors, you cannot know if a return value of 0 is a successful parsing of a string with a value of 0, or if it's an error unless you do some rather laborious checking on the original string you had it parse.
Try to stick with streams, but in situations where you do need to fall back to atoi type functionality, go with the strtoX family of functions (strtoi, strtol, strtof, etc). Or, better yet, if you're using C++11, use the stoX family of functions.
You could use a string tokenizer How do I tokenize a string in C++?
In particular: https://stackoverflow.com/a/55680/2436175
Side note: you do not need to use atoi, you could simply do:
int i,j;
iss >> i >> j;
(but this wouldn't handle alone the problem of optional elements)