Ways of converting wstring to double with error checking - c++

I need to convert a wide string to double number. Presumably, the string is holding a number and nothing else (maybe some whitespaces). If the string contains anything else, an error should be indicated. So I can't use stringstream - it will extract a number without indicating an error if the string contained something else.
wcstod seems like a perfect solution, but it works wrong on Android (GCC 4.8, NDK r9). What other options can I try?

You can use stringstream, then use std:ws to check that any remaining characters on the stream are only whitespace:
double parseNum (const std::wstring& s)
{
std::wistringstream iss(s);
double parsed;
if ( !(iss >> parsed) )
{
// couldn't parse a double
return 0;
}
if ( !(iss >> std::ws && iss.eof()) )
{
// something after the double that wasn't whitespace
return 0;
}
return parsed;
}
int main()
{
std::cout << parseNum(L" 123 \n ") << '\n';
std::cout << parseNum(L" 123 asd \n ") << '\n';
}
prints
$ ./a.out
123
0
(I've just returned 0 in the error case as something quick and easy for my example. You probably want to throw or something).
There are of course other options too. I just felt your assessment was unfair on stringstream. By the way, this is one of the few cases where you actually do want to check eof().
Edit: Ok, I added the ws and Ls to use wchar_ts.
Edit: Here's what the second if conceptually looks like expanded out. May help to understand why it is correct.
if ( iss >> std::ws )
{ // successfully read some (possibly none) whitespace
if ( iss.eof() )
{ // and hit the end of the stream, so we know there was no garbage
return parsed;
}
else
{ // something after the double that wasn't whitespace
return 0;
}
}
else
{ // something went wrong trying to read whitespace
return 0;
}

Related

why does this routine return an array of length 0?

everyone, here is a function I wrote to read a user input which is a vector of double of unknown size, the input must terminate when 'enter' is pressed:
vector<double> read_array()
{
vector<double> array_in;
double el;
while (!cin.get())
{
cin >> el;
array_in.push_back(el);
}
return array_in;
}
To illustrate it consider the following code:
void init() // the function that calls the read_array function
{
cout << "Enter array X: " << endl;
vector<double> X = read_array();
int l = X.size();
cout << l << endl;
}
A typical input when promted is:
1(space)2(space)3(space)4(enter)
When enter is pressed, the input terminates, and the variable 'l' is initialised but is equal to 0
However, when the enter key is pressed, the array size is 0. Debugging it makes it look like it never makes it into the loop like that.
The same routine works well if the input value is not an array.
Thanks to everyone in advance!
I don't know what you hope std::cin.get() does but based on your comment it seems you hope that it somehow deals with end of lines: it doesn't. It simply reads the next character which is unlikely to do you much good. In particular, if the character is anything but '\0' negating it will result in the boolean value false. That said, the loop should in principle work unless you only input a single digit numeric value followed (possibly after space) by a non-digit or the end of the input.
The easiest approach to deal with line-based input is to read the line into a std::string using std::getline() and then to parse the line using std::istringstream:
std::vector<double> read_array() {
std::vector<double> result;
if (std::string line; std::getline(std::cin, line)) {
std::istringstream lin(line);
for (double tmp; std::cin >> tmp; ) {
result.push_back(tmp);
}
}
return result;
}
As std::cin is only involved while reading lines, std::cin.fail() won't be set when parsing doubles fails. That is, you can read multiple lines with arrays of doubles, each of which can also be empty.
If you don't want to read an auxiliary line, you'll need to understand a bit more about how formatted input in C++ works: it starts off skipping whitespace. As newlines are whitespace you need to rather read the whitespace yourself and stop if it happens to be a newline or non-whitespace. I'd use a function doing this skipping which returns false if it reached a newline (which is still extracted):
bool skip_non_nl_ws(std::istream& in) {
for (int c; std::isspace(c = in.peek()); std::cin.ignore()) {
if (c == '\n') {
return false;
}
}
return true;
}
std::vector<double> read_array() {
std::vector<double> result;
for (double tmp; skip_non_nl_ws(std::cin) && std::cin >> result); ) {
result.push_back(tmp);
}
return result;
}
This approach has a similar property that std::ios_base::failbit won't be set. However, if any of the characters on a line can't be parsed as double the bit will set. That way you can detect input errors. The approach using std::getline() will just go on to the next line.

how to use locale independent strtod with error reporting

I am using strtod() to convert string to decimal. Since I need to throw an error for incorrect input/invalid characters in i have no other choice.
However the problem is that strtod() is affected by locales. So a '.' becomes an invalid character when the program is run in a different locale
If I use a solution like this:
std::istringstream text(iterator->second.c_str());
text.imbue(std::locale::classic());
double result;
text >> result;
it is not possible to check for invalid input at all.
Which solution can provide me both?
Yes you can determine if the value provided to the stringstream was completely converted or not. You can test the stream to see if it reached the end of file. If it has then you have read everything and converted it to a double. If not then there was invalid input.
Here is a little sample demonstrating the conversion:
bool convert(const std::string & text)
{
if (text = "")
return false;
std::istringstream iss(text);
iss.imbue(std::locale::classic());
double result;
iss >> result;
if (iss.eof())
return true;
else
return false;
}
int main()
{
std::cout << convert("123.456") << std::endl;
std::cout << convert("123.456abc") << std::endl;
std::cout << convert("123.456e5") << std::endl;
std::cout << convert("e5") << std::endl;
return 0;
}
Output:
1
0
1
0
Live Example
You can check for invalid input by testing text for errors and that it reached the end of input:
text >> result;
bool ok = text && text.eof();
Example: http://coliru.stacked-crooked.com/a/758b769f26567d1e

why float input from file doesnt work c++

The following code is meant to take all floating numbers for calculation from the file "float.txt" The problem is the floating numbers has junk information between it.
example file:
23.5 aujsaN8.2<:::32
After the first floating point is gotten, the while loop never ends, the program no longer gets any information from the file. please help.
int main()
{
float num;
ifstream input("float.txt");
input >> num;
while (!(input.eof()))
{
input >> num;
}
input.close();
return 0;
}
You could do something like this:
decltype(input)::Traits::int_type c;
while ((c = input.peek()) != decltype(input)::Traits::eof())
{
if (std::isdigit(c))
{
input >> num;
... use num ...
}
else
input.get();
}
The idea here is to peek for the next character, and if it's a digit then we know a >> streaming to double will succeed, otherwise we actually get() the character to remove it from the input stream.
It gets trickier if you need to extract negative numbers (hint: one way - use a bool to track if the last value of c seen was -, then have if (the_bool) num = -num; before using num). While the code above handles e.g. X0.23X -> 0.23, you may need or may not need to also handle X.23X - if so, check for . then see if the next character is a digit... the tricky thing is that peeking for the digit means you've already consumed the ., so it won't be there for input >> num... you can try input.putback('.') but I'm not certain it's Standard-required to work when you've consumed a character then peeked - you'd have to check....
Look i don't know how the statement input>>num; works i never used those, instead i will do these in order to extract floats from your specified file.
float floats=0;
char string[100], ch;
while (file.get(ch)!=' ')
{
string[i]=ch;
i++;
}
string[i]='\0';
floats=atof(string);
This program simply copies the characters untill a ' ' (Space) is found, like the file u shown, then the function aotf() converts the string to floating point number.
Is this the answer to your question??, if yes then please vote +1, and if any question u can ask me, i will sure help u...
this works
// g++ -o parse_float_file parse_float_file.cpp -std=c++11
#include <iostream>
#include <fstream>
#include <string>
int main() {
float curr_number;
std::ifstream inFile("float.txt");
std::string line;
while(getline(inFile, line)) {
try {
curr_number = std::stof(line);
std::cout << "-->" << curr_number << "<--" << std::endl;
} catch (const std::exception &e) {
std::cout << "ERROR - not a float : " << line << std::endl;
}
}
return 0;
}

How to raise an error, if the parsed number of a C++ stdlib stream is immediately followed by a non whitespace character?

In the following example, I didn't expect, that 1.2345foo would be parsed. Since I am reading data files, it is probably better to raise an error and notify the user.
Is peek() the correct thing to do here?
#include <iostream>
#include <sstream>
int main()
{
std::stringstream in("1.2345foo");
double x;
in >> x;
if (in) {
std::cout << "good\n";
}
else {
std::cout << "bad\n";
}
}
Output
good
Just look-ahead at the next character. In the case of an
isolated std::istringstream, you can do something like:
if ( in >> x && in.get() == std::string::traits_type::eof() ) {
// good
} else {
// bad
}
(I generally like to separate the tests, in order to give more
meaningfull error messages.)
If you're concerned with data in a larger file, where for
example "1.2345 foo" would be legal, but "1.2345foo" not,
you can use look-ahead, something like:
if ( in >> x
&& (std::isspace( in.peek() )
|| in.peek() == std::string::traits_type::eof()) ) {
// good
} else {
// bad
}
EDIT:
I forgot to mention: in the first case, you might want to skip
white space before checking for eof:
if ( in >> x >> std::ws && in.get() == std::string::traits_type::eof() ) ...
(I do this a lot. If the string input is from std::getline,
you really do want to allow trailing white space, since it is
invisible for anyone looking at the file.)
The easiest way to read it as std::string, and then use std::stod to convert the std::string into double. The conversion function will throw exception for invalid input such as when you pass "1.2345foo" to it.

C++ character to int

what happens when you cin>> letter to int variable? I tried simple code to add 2 int numbers, first read them, than add them. But when I enter letter, it just fails and prints tons of numbers to screen. But what causes this error? I mean, I expected it to load and use ASCII code of that letter.
I assume you have code like this:
int n;
while (someCondition) {
std::cin >> n;
.....
std::cout << someOutput;
}
When you enter something that cannot be read as an integer, the stream (std::cin) enters a failed state and all following attempts at input fail as long as you don't deal with the input error.
You can test the success of an input operation:
if (!(std::cin >> n)) //failed to read int
and you can restore the stream to a good state and discard unprocessed characters (the input that caused the input failure):
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
Another possibility is to accept input into a string variable (which rarely fails) and try to convert the string to int.
This is a very common problem with input and there should be tons of threads about it here and elsewhere.
if you explicitly cast your char to an int, it'll use the ASCII code, else it's not doing the cast by itself to int, so you get the strange results you are getting.
You could use int istream::get();
The issue is that when you read into an integer variable, using C++ streams, the input method expects characters that can make up a number, such as '+', '-', '0' ... '9'. Any character that is not in this set terminates the input and a number is created from these characters.
To get the ASCII value of a character, you need to input the data into a char variable. Internally, it has the ASCII value (provided the program in not reading 16-bit chars or EBCDIC). If you want to see the ASCII value, you will need to output the char variable as an integer (generally requiring a cast before output).
If reading an integer fails, your program needs to inform the User that the input was incorrect. The program may require the User to input the number again before the program can process the input data.
When you are doing something like:
int x;
cin >> x;
You are instructing C++ to expect to read an int from keyboard. Actually, when you enter something not an int, the input stream (standard input, in this case) renders unusable due to its error state.
That's why I personally prefer to always read strings, and convert them to numbers if needed. There are conversion functions in the C part of the C++ standard library that can help with this.
double cnvtToNumber(const std::string &num)
{
char * ptr;
double toret = strtod( num.c_str(), &ptr );
if ( *ptr != 0 ) { // If it got to the end of the string, then it is correct.
throw std::runtime_error( "input was not a number" );
}
return toret;
}
Here you are converting a number, and detecting if the conversion was right. strtod(s) will only stop when finding the end of the string, or a space. In order to avoid trailing spaces fooling the function, you could need a trim function:
std::string &trim(std::string &str)
{
// Remove trailing spaces
unsigned int pos = str.length() -1;
while( str[ pos ] == ' ' ) {
--pos;
}
if ( pos < ( str.length() -1 ) ) {
str.erase( pos + 1 );
}
// Remove spaces at the beginning
pos = 0;
while( str[ pos ] == ' ' ) {
++pos;
}
if ( pos < 1 ) {
str.erase( 0, pos );
}
return str;
}
Now you can safely read from console (or whatever other input stream):
int main()
{
std::string entry;
try {
std::getline( std::cin, entry );
int age = cnvtToNumber( trim( entry ) );
std::cout << age << std::endl;
} catch(const std::exception &e) {
std::cerr << e.what() << std::endl;
}
}
Of course you can lose precision if you always read double's and then convert them. There are specific functions for integers (strtol(s)), in case you want to investigate further.