Using sstream parser integer in a char array - c++

I have trapped for hours when using sstream to parser integer in a char array.
I don't know why there is one more iteration in while loop.
//main.cpp
#include <iostream>
#include <sstream>
int main()
{
char data[5] = "1 23";
//char data[4] = {'1', ' ', '2', '3'}; another attempt
std::stringstream stream;
stream << data;
int count = 1;
while (stream)
{
double x = 0;
stream >> x;
std::cout << count << " " << x << std::endl;
count++;
}
return 0;
}
the program output shows that:
1 1
2 23
3 0
I use the follow command to compile the program.
g++ main.cpp
I think that there are 2 integer,so there are only 2 iteration. I don't know why there are 3 iteration in while loop. I suppose it is because the '\0' at the end of char array, but I tried, it get the same result.
Any Suggestion? Thanks.

You are not checking stream >> x for success:
if (stream >> x)
{
std::cout << count << " " << x << std::endl;
count++;
}
will do the job.
You can also include it in the loop:
double x = 0;
while (stream >> x)
{
std::cout << count << " " << x << std::endl;
count++;
}

I think that there are 2 integer,so there are only 2 iteration. I don't know why there are 3 iteration in while loop.
First iteration: The stream is in a good state. You extract an integer. You print the integer without checking whether the extraction succeeded (it did).
Second iteration: The stream is in a good state. You extract an integer. You print the integer without checking whether the extraction succeeded (it did).
Third iteration: The stream is in a good state. You extract an integer. You print the integer without checking whether the extraction succeeded (it had not).
Fourth iteration: The stream is in a failed state. The loop ends.
The conventional pattern for stream extraction is:
while(stream >> variable) { // or if for single extraction
// use the extracted variable
This way you always check the success of the extraction before using the extracted value.
char data[4] = {'1', ' ', '2', '3'}; another attempt
This attempt has undefined behaviour as soon as you insert the non-null terminated string into the stream.

Related

Numeric input in C++ with trailing chars

I need to fetch user input from std::cin into a variable of type double. This is in the context of complex numbers, so it often happens to have a number input as 5i or 5.4565i.
Consider the following code in the main() function:
while (true) {
double c;
std::cin >> c;
std::cout << c << "\n\n";
}
Here's what happens:
In: 0.45
Out: 0.45
// OK
In: 5 0.45
Out: 5
0.45
// OK
In: 0.45i
Expected out: 0.45
Acutal out: 0
0
0...
I'm guessing this is because It's not recognizing 0.45i as a double. So how can I correctly fetch the value 0.45 from 0.45i, ignoring the trailing i?
Read a string first and then convert to double,
$ cat ans.cpp
#include <cstdlib>
#include <iostream>
int main()
{
std::string str;
double dbl;
while (std::cin >> str) {
dbl = std::strtod(str.data(), NULL);
std::cout << dbl << std::endl;
}
}
First you read each white space separated string into str. The strtod function will try to get as many as characters to form a floating point literate, including hex float. It returns the double parsed from this part of the string. The second parameter can be a char * pointer, which point to one pass the last character that is parsed. It is useful if you do not want to simply discard the trailing characters. It is ignored if it is null.
You can test the state of the input string. If input has failed, just get the offending token in a string and proceed. For example:
double d;
for(;;) {
std::string dummy;
std::cout << "Input :";
std::cin >> d;
if (std::cin.fail()) { // was input numeric?
std::cin.clear();
std::cin >> dummy;
if (dummy == "END") break; // out of the infinite loop...
std::cout << "Non numeric: >" << dummy << "<" << std::endl;
}
else {
std::cout << "Numeric: " << d << std::endl;
}
}
Try reading std::string and then parsing it manually.
One way could be you take input as a string and then analyse it character by character using a for loop. And then convert it into the double while ignoring the i.You could divide the string into three parts i.e integer part, decimal part and i.
And for separating these you could use conditions like if(c[i]=='.') and if(c[i]=='i').

why std::cin doesn't give error on "char"

Here is a small code:
char a;
while(std::cin >> a) {
std::cout << a << " is pressed\n";
}
When I type in "w", i get "w is pressed".
When I type in "www", i get "w is pressed" 3 times in a row.
Can someone please explain why this happens?
Thanks
When you use std::cin to read a char variable it reads one character at a time. That is why you get 3 iterations in the while loop for input www.
There is a queue of inputs. if you entered too much, your input waits in patient...
The first part of the answer is on the first line of your code.
char a;
Variable a is a single char, an 8-bit value typically used to store a code representing a display character. If the display is ASCII, then (value) 0 = no character, (value) 32 = space, value 48 = (character) '0', etc.
std::cin is an instance of class std::istream, it has various members and operator overloads to deal with different types. In the case of a char, you are calling
std::istream::operator(char)
Which reads one char, exactly one, from the input stream and returns.
#include <iostream>
int main()
{
char a, b, c;
std::cin >> a >> b >> c;
std::cout << "a = " << a << ", b = " << b << ", c = " << c << '\n';
return 0;
}

How to convert vector to string and convert back to vector

----------------- EDIT -----------------------
Based on juanchopanza's comment : I edit the title
Based on jrok's comment : I'm using ofstream to write, and ifstream to read.
I'm writing 2 programs, first program do the following tasks :
Has a vector of integers
convert it into array of string
write it in a file
The code of the first program :
vector<int> v = {10, 200, 3000, 40000};
int i;
stringstream sw;
string stringword;
cout << "Original vector = ";
for (i=0;i<v.size();i++)
{
cout << v.at(i) << " " ;
}
cout << endl;
for (i=0;i<v.size();i++)
{
sw << v[i];
}
stringword = sw.str();
cout << "Vector in array of string : "<< stringword << endl;
ofstream myfile;
myfile.open ("writtentext");
myfile << stringword;
myfile.close();
The output of the first program :
Original vector : 10 200 3000 40000
Vector in string : 10200300040000
Writing to File .....
second program will do the following tasks :
read the file
convert the array of string back into original vector
----------------- EDIT -----------------------
Now the writing and reading is fine, thanks to Shark and Jrok,I am using a comma as a separator. The output of first program :
Vector in string : 10,200,3000,40000,
Then I wrote the rest of 2nd program :
string stringword;
ifstream myfile;
myfile.open ("writtentext");
getline (myfile,stringword);
cout << "Read From File = " << stringword << endl;
cout << "Convert back to vector = " ;
for (int i=0;i<stringword.length();i++)
{
if (stringword.find(','))
{
int value;
istringstream (stringword) >> value;
v.push_back(value);
stringword.erase(0, stringword.find(','));
}
}
for (int j=0;j<v.size();i++)
{
cout << v.at(i) << " " ;
}
But it can only convert and push back the first element, the rest is erased. Here is the output :
Read From File = 10,200,3000,40000,
Convert back to vector = 10
What did I do wrong? Thanks
The easiest thing would be to insert a space character as a separator when you're writing, as that's the default separator for operator>>
sw << v[i] << ' ';
Now you can read back into an int variable directly, formatted stream input will do the conversion for you automatically. Use vector's push_back method to add values to it as you go.
Yes, this question is over a year old, and probably completely irrelevant to the original asker, but Google led me here so it might lead others here too.
When posting, please post a complete minimal working example, having to add #include and main and stuff is time better spent helping. It's also important because of your very problem.
Why your second code isn't working is all in this block
for (int i=0;i<stringword.length();i++)
{
if (stringword.find(','))
{
int value;
istringstream (stringword) >> value;
v.push_back(value);
stringword.erase(0, stringword.find(','));
}
}
istringstream (stringword) >> value interprets the data up to the comma as an integer, the first value, which is then stored.
stringword.find(',') gets you the 0-indexed position of the comma. A return value of 0 means that the character is the first character in the string, it does not tell you whether there is a comma in the string. In that case, the return value would be string::npos.
stringword.erase deletes that many characters from the start of the string. In this case, it deletes 10, making stringword ,200,3000,40000. This means that in the next iteration stringword.find(',') returns 0.
if (stringword.find(',')) does not behave as wished. if(0) casts the integer to a bool, where 0 is false and everything else is true. Therefore, it never enters the if-block again, as the next iterations will keep checking against this unchanged string.
And besides all that there's this:
for (int j=0;j<v.size();i++)
{
cout << v.at(i) << " " ;
}
it uses i. That was declared in a for loop, in a different scope.
The code you gave simply doesn't compile, even with the added main and includes. Heck, v isn't even defined in the second program.
It is however not enough, as the for condition stringword.length() is recalculated every loop. In this specific instance it works, because your integers get an extra digit each time, but let's say your input file is 1,2,3,4,:
The loop executes normally three times
The fourth time, stringword is 4, stringword.length() returns 2, but i is already valued 3, so i<stringword.length() is invalid, and the loop exits.
If you want to use the string's length as a condition, but edit the string during processing, store the value before editing. Even if you don't edit the string, this means less calls to length().
If you save length beforehand, in this new scenario that would be 8. However, after 4 loops string is already empty, and it executes the for loop some more times with no effect.
Instead, as we are editing the string to become empty, check for that.
All this together makes for radically different code altogether to make this work:
while (!stringword.empty())
{
int value;
istringstream (stringword) >> value;
v.push_back(value);
stringword.erase(0, stringword.find(',')+1);
}
for (int i = 0; i < v.size(); i++)
{
cout << v.at(i) << " " ;
}
A different way to solve this would have been to not try to find from the start, but from index i onwards, leaving a string of commas. But why stick to messy stuff if you can just do this.
And that's about it.

This is not the output i asked for

#include <fstream>
#include <iostream>
#include <string>
using namespace std;
int main() {
ofstream w("d:/tester.txt");
int f = 1;
int s = 2;
int t = 3;
string x = "hello";
w << f << endl << s << endl << t << endl << x ;
w.close();
ifstream r("d:/tester.txt");
r >> x;
cout << x << endl ;
s = s + 10 ;
r.close();
/* ofstream wa("d:/tester.txt");
wa << s;
wa.close();*/
}
I always get the output equal to 1 .
Why is this so ? When i asked for the string hello 1 gets displayed .
In fact, it is the output you ask for: you are reading the first string token from the file. And that happens to be the number “1” you wrote on the first line into the file.
The streaming operators don’t magically parse your file for the most suitable token; they are simply reading the next available token. And even if they did, “1” would be a perfectly valid choice for a string.
You store consecutively 1 2 3 in your file, then you fetch the first value from the file. Are you surprised that the value is 1? If you want the other values, you must call the stream in functions more than once.
The first line of tester.txt is f which is 1.
x is a string, so when you read from tester.txt using r>>x you get the first line, which is "1"
When you open a stream, you open it in a top-bottom fashion, this meaning that the stream is positioned in the beginning of the first line.
You wrote:
w << f << endl << s << endl << t << endl << x ;
So line the lines are:
f
s
t
x
And f = 1, so you are getting what you should be getting.
Isn't that what you would expect, or am I missing something?
You put the values "1" "2" "3" "hello" in the stream, in that order.
Then, you stream from that into a string. String spec says that all characters will be copied until the first valid whitespace. It will see "1" as a char, and then stop at the newline. Hence, you will get a string, the string "1";

isdigit() c++, probably simple question, but stuck

I'm having trouble with isdigit. I read the documentation, but when I cout << isdigit(9), I get a 0. Shouldn't I get a 1?
#include <iostream>
#include <cctype>
#include "Point.h"
int main()
{
std::cout << isdigit(9) << isdigit(1.2) << isdigit('c');
// create <int>i and <double>j Points
Point<int> i(5, 4);
Point<double> *j = new Point<double> (5.2, 3.3);
// display i and j
std::cout << "Point i (5, 4): " << i << '\n';
std::cout << "Point j (5.2, 3.3): " << *j << '\n';
// Note: need to use explicit declaration for classes
Point<int> k;
std::cout << "Enter Point data (e.g. number, enter, number, enter): " << '\n'
<< "If data is valid for point, will print out new point. If not, will not "
<< "print out anything.";
std::cin >> k;
std::cout << k;
delete j;
}
isdigit() is for testing whether a character is a digit character.
If you called it as isdigit('9'), it would return nonzero.
In the ASCII character set (which you are likely using), 9 represents the horizontal tab, which is not a digit.
Since you are using the I/O streams for input, you don't need to use isdigit() to validate the input. The extraction (i.e., the std::cin >> k) will fail if the data read from the stream is not valid, so if you are expecting to read an int and the user enters "asdf" then the extraction will fail.
If the extraction fails, then the fail bit on the stream will be set. You can test for this and handle the error:
std::cin >> k;
if (std::cin)
{
// extraction succeeded; use the k
}
else
{
// extraction failed; do error handling
}
Note that the extraction itself also returns the stream, so you can shorten the first two lines to be simply:
if (std::cin >> k)
and the result will be the same.
isdigit() takes an int which is the representation of the character. Character 9 is (assuming you're using ASCII) the TAB character. Character 0x39 or '9' (not 9) is the actual character representing the digit 9.
The digit characters are integer codes 0x30 through 0x39 (or 48 through 57) in ASCII - I reiterate that since ASCII is not a requirement of the ISO C standard. Hence the following code:
if ((c >= 0x30) && (c <= 0x39))
which I've seen before, is not a good idea for portability since there is at least one implementation that uses EBCDIC under the covers - isdigit is the best option in all situations.
isdigit() works on characters, not ascii values, which you are currently passing. Try using isdigit('9').