I have inherited a template to convert a string to a numerical value, and want to apply it to convert to boolean. I am not very experienced with the stringstream and locale classes. I do seem to be getting some odd behaviour, and I am wondering if someone could please explain it to me?
template<typename T> T convertFromString( const string& str ) const {
std::stringstream SStream( str );
T num = 0;
SStream >> num;
return num;
}
This works fine until I try the boolean conversion
string str1("1");
int val1 = convertFromString<int>(str1); // ok
string str2("true");
bool val2 = convertFromString<bool>(str2); // val2 is _false_
I spent some time tracking down the problem. I have confirmed that the locale's truename() returns "true".
The problem seems to be with the initialisation of the variable num. I can change the template to this and it works:
template<typename T> T convertFromString( const string& str ) const {
std::stringstream SStream( str );
T num; // <----------------------- Changed here
SStream >> num;
return num;
}
string str2("true");
bool val2 = convertFromString<bool>(str2); // val2 is _true_
Why does it work? I accept that initialising a bool with '0' is wrong, but why would this cause the SStream>>numconversion to fail?
Initialising a bool with 0 will reliably set it to false, and this has no effect on the stream extraction.
What is causing your problem is that streams by default only recognize the values 0 and 1 when dealing with booleans. To have them recognize the names true and false, you need to tell that explicitly to the stream with the boolalpha manipulator.
The best way to solve your problems is to specialize the template for bool:
template<> bool convertFromString<bool>( const string& str ) const {
std::stringstream SStream( str );
bool val = false;
SStream >> val;
if( SStream.fail() )
{
SStream.clear();
SStream >> boolalpha >> val;
}
return val;
}
Note that your change did not make the code work. It just appeared to do so for the single testcase you used.
With your change, the function failed to read from the stream and returned an uninitialised value. As any non-zero value will be interpreted as true, the function appears to work, but as soon as you try to extract "false", you will see it fail (the function still returns true).
Edit: Adapted the code to handle both numeric and alpha bools.
This is because the stringstream conversion from 'true' fails - your function template should check SStream.fail() before it returns, so you can discover similar failures more easily.
When you init the bool its value is false. When you don't init it (after you remove the = 0), it is random garbage (usually non-zero) and returns true.
template<typename T> T convertFromString( const string& str ) {
std::stringstream SStream( str );
T num = 0;
SStream >> num;
if (SStream.fail())
{
// conversion failure - handle error
bool error = true;
}
return num;
}
Related
I'm creating an encryption/decryption program in C++, and I use three user-provided numbers to customize the encryption. I read about isdigit() on cplusplus.com, and made a function based on that:
bool is_numeric(char *string)
{
int sizeOfString = sizeof(string);
int iteration = 0;
bool isNumeric = true;
while(iteration < sizeOfString)
{
if(!isdigit(string[iteration]))
{
isNumeric = false;
break;
}
iteration++;
}
return isNumeric;
}
However, it doesn't seem to work. Whether I give it a number, or a non-numeric character, it still returns false. What is wrong with my approach.
I think I'd use a standard algorithm:
bool is_numeric(char const *string)
{
return std::all_of(string, string+strlen(string),
[](unsigned char c) { return ::isdigit(c); });
}
Note that as it stands, your code can (often will) have undefined behavior (if the string contains anything that works out as a negative number when encoded into a char). This code prevents that by converting the char to an unsigned char as it's passed to the lambda -- that's why I used a lambda instead of just passing ::isdigit as the predicate to all_of.
You are computing the sizeOfString wrong. Try this instead.
bool is_numeric(char *string)
{
int sizeOfString = strlen(string);
int iteration = 0;
bool isNumeric = true;
while(iteration < sizeOfString)
{
if(!isdigit(string[iteration]))
{
isNumeric = false;
break;
}
iteration++;
}
return isNumeric;
}
You may want to add functionality to check for the . character as well! Right now your code only returns true if your string is an integer.
while ('0' <= *string && *string <= '9')
++string;
return *string == '\0';
or, if you prefer using isdigit:
while (is digit((int)*string))
++string;
return *string == '\0';
Another possible solution is using a stringstream:
bool isNumeric(const string& s) {
stringstream ss(s);
int val;
ss >> val;
return ! ss.fail() && ss.eof();
}
stringstream::operator>>(int&) will make the stringstream's failbit to be set if the given string is not numeric, and you need to check if all that's in the string is exactly one integer (and nothing else), so you also test for the eof bit.
This also works for negative numbers, and you can also change the int to double if you want to accept floating point numbers.
I have this piece of code :
if(flag == 0)
{
// converting string value to integer
istringstream(temp) >> value ;
value = (int) value ; // value is a
}
I am not sure if I am using the istringstream operator right . I want to convert the variable "value" to integer.
Compiler error : Invalid use of istringstream.
How should I fix it ?
After trying to fix with the first given answer . it's showing me the following error :
stoi was not declared in this scope
Is there a way we can work past it . The code i am using right now is :
int i = 0 ;
while(temp[i] != '\0')
{
if(temp[i] == '.')
{
flag = 1;
double value = stod(temp);
}
i++ ;
}
if(flag == 0)
{
// converting string value to integer
int value = stoi(temp) ;
}
Unless you really need to do otherwise, consider just using something like:
int value = std::stoi(temp);
If you must use a stringstream, you typically want to use it wrapped in a lexical_cast function:
int value = lexical_cast<int>(temp);
The code for that looks something like:
template <class T, class U>
T lexical_cast(U const &input) {
std::istringstream buffer(input);
T result;
buffer >> result;
return result;
}
As to how to imitation stoi if your don't have one, I'd use strtol as the starting point:
int stoi(const string &s, size_t *end = NULL, int base = 10) {
return static_cast<int>(strtol(s.c_str(), end, base);
}
Note that this is pretty much a quick and dirty imitation that doesn't really fulfill the requirements of stoi correctly at all. For example, it should really throw an exception if the input couldn't be converted at all (e.g., passing letters in base 10).
For double you can implement stod about the same way, but using strtod instead.
First of all, istringstream is not an operator. It is an input stream class to operate on strings.
You may do something like the following:
istringstream temp(value);
temp>> value;
cout << "value = " << value;
You can find a simple example of istringstream usage here: http://www.cplusplus.com/reference/sstream/istringstream/istringstream/
Hey im validating a string.
string getString(string q)
{
string input;
do
{
cout << q.c_str() << endl;
cin >> input;
} while (!isalpha(input));
return input;
}
When using while(!isalpha(input)); input gives that error.
Can anyone help me with this?
The other answer describes what the problem is, but here's a solution that makes use of algorithms from the standard library instead of writing your own (example requires C++11)
bool all_alpha( std::string const& s )
{
return std::all_of( s.cbegin(), s.cend(), static_cast<int(*)(int)>(std::isalpha) );
}
The above function will return true only if all characters in the string are alphabetic. If you only want to disallow numeric characters, I'd use a slightly different function.
bool any_digit( std::string const& s )
{
return std::any_of( s.cbegin(), s.cend(), static_cast<int(*)(int)>(std::isdigit) );
}
or
bool no_digits( std::string const& s )
{
return std::none_of( s.cbegin(), s.cend(), static_cast<int(*)(int)>(std::isdigit) );
}
Use these functions to validate the input that you receive from the user.
If you can't use C++11 features, the functions can be modified to use std::find_if instead, and compare the return value of find_if to s.end() to determine success / failure.
The isalpha function takes an integer as a parameter, but you are passing it a std::string. You could write a function like this to test if your string contains only alphabetical characters:
bool noDigitInString(std::string str)
{
for (int i = 0; i < str.size(); i++)
{
if (isdigit(str[i]))
return false;
}
return true;
}
When I use getline, I would input a bunch of strings or numbers, but I only want the while loop to output the "word" if it is not a number.
So is there any way to check if "word" is a number or not? I know I could use atoi() for
C-strings but how about for strings of the string class?
int main () {
stringstream ss (stringstream::in | stringstream::out);
string word;
string str;
getline(cin,str);
ss<<str;
while(ss>>word)
{
//if( )
cout<<word<<endl;
}
}
Another version...
Use strtol, wrapping it inside a simple function to hide its complexity :
inline bool isInteger(const std::string & s)
{
if(s.empty() || ((!isdigit(s[0])) && (s[0] != '-') && (s[0] != '+'))) return false;
char * p;
strtol(s.c_str(), &p, 10);
return (*p == 0);
}
Why strtol ?
As far as I love C++, sometimes the C API is the best answer as far as I am concerned:
using exceptions is overkill for a test that is authorized to fail
the temporary stream object creation by the lexical cast is overkill and over-inefficient when the C standard library has a little known dedicated function that does the job.
How does it work ?
strtol seems quite raw at first glance, so an explanation will make the code simpler to read :
strtol will parse the string, stopping at the first character that cannot be considered part of an integer. If you provide p (as I did above), it sets p right at this first non-integer character.
My reasoning is that if p is not set to the end of the string (the 0 character), then there is a non-integer character in the string s, meaning s is not a correct integer.
The first tests are there to eliminate corner cases (leading spaces, empty string, etc.).
This function should be, of course, customized to your needs (are leading spaces an error? etc.).
Sources :
See the description of strtol at: http://en.cppreference.com/w/cpp/string/byte/strtol.
See, too, the description of strtol's sister functions (strtod, strtoul, etc.).
The accepted answer will give a false positive if the input is a number plus text, because "stol" will convert the firsts digits and ignore the rest.
I like the following version the most, since it's a nice one-liner that doesn't need to define a function and you can just copy and paste wherever you need it.
#include <string>
...
std::string s;
bool has_only_digits = (s.find_first_not_of( "0123456789" ) == std::string::npos);
EDIT: if you like this implementation but you do want to use it as a function, then this should do:
bool has_only_digits(const string s){
return s.find_first_not_of( "0123456789" ) == string::npos;
}
You might try boost::lexical_cast. It throws an bad_lexical_cast exception if it fails.
In your case:
int number;
try
{
number = boost::lexical_cast<int>(word);
}
catch(boost::bad_lexical_cast& e)
{
std::cout << word << "isn't a number" << std::endl;
}
If you're just checking if word is a number, that's not too hard:
#include <ctype.h>
...
string word;
bool isNumber = true;
for(string::const_iterator k = word.begin(); k != word.end(); ++k)
isNumber &&= isdigit(*k);
Optimize as desired.
Use the all-powerful C stdio/string functions:
int dummy_int;
int scan_value = std::sscanf( some_string.c_str(), "%d", &dummy_int);
if (scan_value == 0)
// does not start with integer
else
// starts with integer
You can use boost::lexical_cast, as suggested, but if you have any prior knowledge about the strings (i.e. that if a string contains an integer literal it won't have any leading space, or that integers are never written with exponents), then rolling your own function should be both more efficient, and not particularly difficult.
Ok, the way I see it you have 3 options.
1: If you simply wish to check whether the number is an integer, and don't care about converting it, but simply wish to keep it as a string and don't care about potential overflows, checking whether it matches a regex for an integer would be ideal here.
2: You can use boost::lexical_cast and then catch a potential boost::bad_lexical_cast exception to see if the conversion failed. This would work well if you can use boost and if failing the conversion is an exceptional condition.
3: Roll your own function similar to lexical_cast that checks the conversion and returns true/false depending on whether it's successful or not. This would work in case 1 & 2 doesn't fit your requirements.
Here is another solution.
try
{
(void) std::stoi(myString); //cast to void to ignore the return value
//Success! myString contained an integer
}
catch (const std::logic_error &e)
{
//Failure! myString did not contain an integer
}
Since C++11 you can make use of std::all_of and ::isdigit:
#include <algorithm>
#include <cctype>
#include <iostream>
#include <string_view>
int main([[maybe_unused]] int argc, [[maybe_unused]] char *argv[])
{
auto isInt = [](std::string_view str) -> bool {
return std::all_of(str.cbegin(), str.cend(), ::isdigit);
};
for(auto &test : {"abc", "123abc", "123.0", "+123", "-123", "123"}) {
std::cout << "Is '" << test << "' numeric? "
<< (isInt(test) ? "true" : "false") << std::endl;
}
return 0;
}
Check out the result with Godbolt.
template <typename T>
const T to(const string& sval)
{
T val;
stringstream ss;
ss << sval;
ss >> val;
if(ss.fail())
throw runtime_error((string)typeid(T).name() + " type wanted: " + sval);
return val;
}
And then you can use it like that:
double d = to<double>("4.3");
or
int i = to<int>("4123");
I have modified paercebal's method to meet my needs:
typedef std::string String;
bool isInt(const String& s, int base){
if(s.empty() || std::isspace(s[0])) return false ;
char * p ;
strtol(s.c_str(), &p, base) ;
return (*p == 0) ;
}
bool isPositiveInt(const String& s, int base){
if(s.empty() || std::isspace(s[0]) || s[0]=='-') return false ;
char * p ;
strtol(s.c_str(), &p, base) ;
return (*p == 0) ;
}
bool isNegativeInt(const String& s, int base){
if(s.empty() || std::isspace(s[0]) || s[0]!='-') return false ;
char * p ;
strtol(s.c_str(), &p, base) ;
return (*p == 0) ;
}
Note:
You can check for various bases (binary, oct, hex and others)
Make sure you don't pass 1, negative value or value >36 as base.
If you pass 0 as the base, it will auto detect the base i.e for a string starting with 0x will be treated as hex and string starting with 0 will be treated as oct. The characters are case-insensitive.
Any white space in string will make it return false.
I have a string of class string
string str;
how can I check if it is a number or not, str can only have 3 possible types described below like
abcd
or a number like
123.4
or a number with a parenthesis attach to the end it for example
456)
note the parenthesis at the end of "str" is the only possible combination of number and none number
where the bottom two are considered valid numbers, I know I could use lexical_cast if only the first 2 cases occur, but how about considering all 3 possible cases to occur?
I don't need to do anything fancy with str, I just need to know whether it is a valid number as I described
The C++ solution for parsing strings manually is string streams. Put your string into a std::istringstream and read from that.
What you could do to parse this is to try to read an (unsigned) int from the string.
If this fails, it is a string not starting with digits.
If it works, peek at the next character. If that's a . you have a floating point number, if it's a ), you have an integer number. (Otherwise you have a reading error.)
Something along the lines of
void read(const std::string& str)
{
std::istringstream iss(str);
int i;
if( !(iss>>i) ) {
// str contains "abcd"
} else {
switch( iss.peek() ) {
case ')':
// i contains '456'
break;
case '.' {
double d;
if( !(iss>>d) ) throw "dammit!";
d += i;
// d contains floating point value
break;
default: throw "what?!";
}
// ...
}
Does this make sense?
You have to define "number". There isn't a generic "number" type (for example, your first number is a double while the second is an integer).
That said, as shown in this answer, lexical_cast simply checks that the destination and only the destination exists:
template <typename R, typename T>
R lexical_cast(const T& pX)
{
std::stringstream ss;
ss << pX;
R result; // take out any whitespace, make sure nothing is left
if ((ss >> result).fail() || !(ss >> std::ws).eof())
{
throw std::bad_cast();
}
return result;
}
So just make a new function:
template <typename R, typename T>
R weak_lexical_cast(const T& pX)
{
std::stringstream ss;
ss << pX;
R result;
if ((ss >> result).fail())
{
throw std::bad_cast();
}
return result;
}
It's basically a lexical_cast but doesn't care about any remaining characters (allowing your second number to work.)
maybe a regular expression would do the trick for you.