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)
Related
I should get multiples lines of input from cin and I need to take only 2 integers for each lines.
I used while(getline(cin, input)) and istringstream iss(input) to handle errors.
and I used the return value of iss >> d (double type variable), iss >> s (string type variable)
to handle the error like ( 1 2 extrainput ; that is, integer integer string)
because I thought that there is no other case that input value is neither double nor string
Is there any other elegant way to limit the number of inputs for each line?
(If there are more than 3 input values, it should be considered as error case)
The most elegant way I know is just to wrap it in operator>> with a custom type.
std::istream& operator>>(std::istream& stream, int_pair& pair) {
std::string tempstring;
if (!std::getline(stream, tempstring)) return stream;
std::stringstream line(tempstring);
int_pair temppair; //temp so that invalid end of line doesn't modify parameter
line >> temppair.first;
line >> temppair.second;
std::getline(line, tempstring);
if (!tempstring.empty()) stream.setstate(std::ios::failbit);
pair = temppair;
return stream;
}
Then usage becomes elegant
int main(){
int_pair one_pair;
std::cin >> one_pair;
std::cout << one_pair;
std::vector<int_pair> vector;
std::copy(std::istream_iterator<int_pair>(std::cin), {}, std::back_inserter(vector));
std::copy(vector.begin(), vector.end(), std::ostream_iterator<int_pair>(std::cout));
}
http://coliru.stacked-crooked.com/a/0ceb5b7922ac03b5
(I have a bug somewhere, the second call to getline on the input is failing and I don't know why, but the point is valid regardless)
I've attempted to use atof() (which I think is way off) and stringstream. I feel like stringstream is the answer, but I'm so unfamiliar with it. Based on some Google searches, YouTube videos, and some time at cplusplus.com, my syntax looks like below. I'm pulling data from a .csv file and attempting to put it into a std::vector<double>:
while (file.good() )
{
getline(file,line,',');
stringstream convert (line);
convert = myvector[i];
i++;
}
If you are reading doubles from a stream (file) we can simplify this:
double value;
while(file >> value) {
myvector.push_back(value);
}
The operator>> will read from a stream into the type you want and do the conversion automatically (if the conversions exists).
You could use a stringstream as an intermediate if each line had more information on it. Like a word an integer and a double.
std::string line;
while(std::getline(file, line)) {
std::stringstream lineStream(line);
std::string word;
int integer;
double real;
lineStream >> word >> integer >> real;
}
But this is overkill if you just have a single number on each line.
Now lets look at a csv file.
This is a line based file but each value is seporated by ,. So here you would read a line then loop over that line and read the value followed by the comma.
std::string line;
while(std::getline(file, line)) {
std::stringstream lineStream(line);
double value;
char comma;
while(lineStream >> value) {
// You successfully read a value
if (!(lineStream >> comma && comma == ',')) {
break; // No comma so this line is finished break out of the loop.
}
}
}
Don't put a test for good() in the while condition.
Why is iostream::eof inside a loop condition considered wrong?
Also worth a read:
How can I read and parse CSV files in C++?
I am receiving user input via inputstream and am storing it in a array, the below code describes my scenario:
istringstream iss(line);
// The below code is incorrect
while (iss >> i)
{
image[j].r = i;
image[j].g = i;// this should be the next character
image[j].b = i;// this should be the character after the previous one
j++
}
Problem: I want to read in the characters 3 at a time and store them in my array structure. How can I achieve this?
Note: image is an array of a certain kind of structures
You should just read the values you want directly:
size_t j = 0;
istringstream iss(line);
while (iss >> image[j].r >> image[j].g >> image[j].b) {
j++;
}
You need to ensure that image is big enough to hold all the values read.
The probably more flexible solution would be to use a std::vector instead of a raw array:
istringstream iss(line);
std::vector<MyStruct> image;
MyStruct item;
while (iss >> item.r >> item.g >> item.b) {
image.push_back(item);
}
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...
}
I'm using boost libs for c++ and the function lexical_cast behaves really weird. If I do lexical_cast("0.07513994") it works fine, but if I use my variable which I need to convert, it throws the bad_lexical_cast exception. Here is the code:
string word;
istringstream iss(line);
do
{
string word;
iss >> word;
double x;
x = lexical_cast<double>(word);
cout << x << endl;
} while (iss);
What am I doing wrong here? I appreciate any help, thanks
Your problem is probably that the loop is processed one more time than you expect.
The last time through the loop, the read to word fails, setting the fail bit in iss, which is what while(iss) is checking. To fix it you need to do something like this.
string word;
istringstream iss(line);
do
{
string word;
iss >> word;
if(iss)
{
double x;
x = lexical_cast<double>(word);
cout << x << endl;
}
} while (iss);
Unlike functions such as atof() which stop parsing as soon as they see an invalid character, lexical_cast requires that every character in the input string be valid. i.e. any leading or trailing spaces will cause it to throw an exception.
You want to see what kind of input it is getting and trim it accordingly. You should also catch bad_lexical_cast just in case it does get input which is completely garbage.
One possible solution is to use boos.regex or boost.xpressive to extract a valid sub-string and pass the result to lexical_cast.
The problem is probably that you are sending an empty string when there is no data left.
You should change the loop you are using.
Use the while {} loop (not the 'do while' loop). This allows you to read from the stream and test it in a single easy to read statement. Note the result of iss >> word is the stream. When used in this boolean context it is tested to see if the state is good and its value converted into something that can be used by the while condition. Thus if the operator >> filed to work correctly then the loop is never entered.
istringstream iss(line);
string word;
while(iss >> word)
{
double x = lexical_cast<double>(word);
cout << x << endl;
}
But really you don't even need the lexical cast in this situation (unless you want to test for non numbers with an exception). The standard stream operator will convert the input into a double.
istringstream iss(line);
double word;
while(iss >> word)
{
cout << word << endl;
}
if (iss.fail())
{ /* Failure to convert input to a double */
}