可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have been up all night searching for a way to determine if my string value is a valid double and I haven't found a way that will also not reject a number with a point in it...
In my searches I found this
How to determine if a string is a number with C++?
and the answer that Charles Salvia gave was
bool is_number(const std::string& s)
{
std::string::const_iterator it = s.begin();
while (it != s.end() && std::isdigit(*it)) ++it;
return !s.empty() && it == s.end();
}
this works for any number that doesn't have a point in it but a number with a point gets rejected...
回答1:
You may be tempted to use std::stod
like this:
bool is_number(const std::string& s)
{
try
{
std::stod(s);
}
catch(...)
{
return false;
}
return true;
}
but this can be quite inefficient, see e.g. zero-cost exceptions.
So using @Robinson's solution or strtod
is a better option:
bool is_number(const std::string& s)
{
char* end = 0;
double val = strtod(s.c_str(), &end);
return end != s.c_str() && val != HUGE_VAL;
}
回答2:
Why not just use istringstream?
#include <sstream>
bool is_numeric (std::string const & str)
{
auto result = double();
auto i = std::istringstream(str);
i >> result;
i >> std::ws;
return !i.fail() && i.eof();
}
回答3:
You can also count how many points your string contains. If this number is less or equal than 1 and if all other characters are numbers, your string is a valid double.
bool isnumber(const string& s)
{
int nb_point=0;
for (int i=0; i<s.length();i++)
{
if (s[i]=='.')
{
nb_point++;
}
else if (!isdigit(s[i])
{
return false;
}
}
if (nb_point<=1)
{
return true;
}
else
{
return false;
}
}
You can also use a regex if you know how to deal with that...
回答4:
You could use std::istringstream()
. It tells you if there is any non-numerics following the digits by not setting the eof()
flag.
bool is_number(const std::string& s)
{
long double ld;
return((std::istringstream(s) >> ld >> std::ws).eof());
}
int main()
{
std::cout << std::boolalpha << is_number(" 3.14") << '\n';
std::cout << std::boolalpha << is_number(" 3.14x") << '\n';
std::cout << std::boolalpha << is_number(" 3.14 ") << '\n';
}
Output:
true
false
true
Templated Version: For testing specific types
template<typename Numeric>
bool is_number(const std::string& s)
{
Numeric n;
return((std::istringstream(s) >> n >> std::ws).eof());
}
int main()
{
std::cout << std::boolalpha << is_number<int>("314") << '\n';
std::cout << std::boolalpha << is_number<int>("3.14") << '\n';
std::cout << std::boolalpha << is_number<float>(" 3.14") << '\n';
std::cout << std::boolalpha << is_number<double>(" 3.14x") << '\n';
std::cout << std::boolalpha << is_number<long double>(" 3.14 ") << '\n';
}
Output:
true
false
true
false
true
回答5:
Making sure there is at most one dot in the number.
bool is_number(const std::string& s)
{
if (s.empty())
return false;
bool sawDot = false;
for (char c : s )
{
if ( !(std::isdigit(c) || (c == '.' && !sawDot) ) )
return false;
sawDot = sawDot || (c == '.');
}
return true;
}
回答6:
Add another check c == '.'
.
bool is_number(const std::string& s)
{
return !s.empty() && std::find_if(s.begin(),
s.end(), [](char c) { return !(std::isdigit(c) || c == '.'); }) == s.end();
}
You can make the code easier to read by using:
bool is_number(const std::string& s)
{
int dotCount = 0;
if (s.empty())
return false;
for (char c : s )
{
if ( !(std::isdigit(c) || c == '.' ) && dotCount > 1 )
{
return false;
}
dotCount += (c == '.');
}
return true;
}
回答7:
I am very strongly against using a try-catch approach for such a low level operation.
Instead I am using myself the following:
bool const hasDoubleRepresentation( std::string const & str, double & d ) const {
std::stringstream sstr(str);
return !((sstr >> std::noskipws >> d).rdstate() ^ std::ios_base::eofbit);}
The above has the advantage that if it returns true, it also sets d to the number contained in str. If this additional functionality is not required, then one may remove the d as an input argument.
Also note that the presence of noskipws (no skip whitespace) has the effect that it returns false if str is something like " 3.14 ", i.e. a number containing white space.
回答8:
There have been a few answers posted to this, so I figured I would add my own. If you have access to C++17, using charconv may possibly be a more efficient way. I have not benchmarked this, but for most string-conversion applications, charconv is very efficient. This solution also requires a compiler that supports charconv to/from a double. VC++ supports this; I'm not sure which other compilers have implemented this aspect of the standard library.
#include <string>
#include <charconv>
bool string_is_number(const std::string& str)
{
if (str.empty()) return false;
double result;
auto[ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), result);
return (ec == std::errc());
}
Upon a successful conversion, std::from_chars returns std::errc(). This is in essence simply wrapping from_chars and discarding the resulting double. Depending on your use case, it might be more advantageous to take the error value and double (assuming the string will be converted to a number later on), but at that point it would make more sense to use use from_chars by itself.