如何使用C ++确定字符串是否为数字?


136

尝试编写一个检查字符串是否为数字的函数时,我遇到了很多麻烦。对于我正在编写的游戏,我只需要检查正在读取的文件中的一行是否是数字(我会以这种方式知道它是否是参数)。我写了下面的函数,我认为它运行正常(或者我不小心对其进行了编辑以使其停止工作,或者我是精神分裂症患者,或者Windows是精神分裂症患者):

bool isParam (string line)
{
    if (isdigit(atoi(line.c_str())))
        return true;

    return false;
}

184
讨厌看到if (expr) return true; return false;!只是写return expr;
短暂的2011年

17
@ephemient我的风格和您一样。但这真的很重要吗?
布伦南·文森特

2
您的函数原型似乎不合适。为什么不使用bool isParam(const string&line)
MikimotoH

4
是的 学习一种新语言时,我有一个不好的习惯:编写长代码。我是C ++的新手,对“快捷方式”(或可感知的快捷方式)更加犹豫。
布伦丹·温斯坦

57
@Brennan Vincent:是的,这很重要。这是同一类错误作为if (expr) return expr; else return expr;if (expr == true)(if expr != false),或if ((expr == true) == true)。它们都引入了复杂性,不利于代码的编写者,阅读者或编译器。消除不必要的复杂性不是捷径。这是编写更好的软件的关键。
MSalters 2011年

Answers:


148

最有效的方法是仅遍历字符串,直到找到非数字字符为止。如果有任何非数字字符,则可以认为字符串不是数字。

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();
}

或者,如果您想以C ++ 11的方式进行操作:

bool is_number(const std::string& s)
{
    return !s.empty() && std::find_if(s.begin(), 
        s.end(), [](unsigned char c) { return !std::isdigit(c); }) == s.end();
}

如以下注释中所指出的,这仅适用于正整数。如果需要检测负整数或分数,则应使用基于数据库的更强大的解决方案。虽然,添加对负整数的支持很简单。


6
也不能处理负数和非整数。我们不知道该问题基于什么要求。
布伦南·文森特

76
您也可以使用!s.empty() && s.find_first_not_of("0123456789") == std::string::npos;C ++ 03单线。
kbjorklu 2011年

8
也不能处理小数,例如:1.23
littlecodefarmer758 2013年

3
@Remy Lebeau,是的。它实际上并不是将字符串转换为int。它只是在确定字符串是否由数字组成。字符串有多长都没关系。
查尔斯·萨尔维亚

5
不要忘了,包括<string> <algorithm><cctype>使C ++ 11例的工作。
kR105 2014年

88

为什么要重新发明轮子?C标准库(也可以在C ++中使用)具有一个功能可以完全做到这一点:

char* p;
long converted = strtol(s, &p, 10);
if (*p) {
    // conversion failed because the input wasn't a number
}
else {
    // use converted
}

如果要处理分数或科学计数法,请strtod改用(得到double结果)。

如果要允许使用C / C ++样式("0xABC")的十六进制和八进制常量,请0改用最后一个参数。

您的函数可以写成

bool isParam(string line)
{
    char* p;
    strtol(line.c_str(), &p, 10);
    return *p == 0;
}

4
此功能放弃前面的空白。因此,您必须检查ischar的第一个字符。
chmike

1
@chmike:根据我对问题的理解,丢弃前导空格是正确的行为(atoi在问题中也是如此)。
Ben Voigt

1
问题没有明确指定,但我对要求“检查字符串是否为数字”的理解意味着整个字符串为数字,因此没有空格。我感到有必要指出,在这方面,您的答案与其他答案不同。如果字符串的数字前面可以有空格,那么您的答案可能会很好。
chmike

1
@BenVoigt你说,p将被设置为nullptr,如果strtol是SUCESSFUL,对不对?这不是我所看到的:(
乔纳森·梅2014年

2
@JonathanMee:不,p将指向终止字符串的NUL。所以p != 0*p == 0
Ben Voigt 2014年

33

对于C ++ 11编译器,对于非负整数,我将使用类似以下内容(请注意,::而不是std::):

bool is_number(const std::string &s) {
  return !s.empty() && std::all_of(s.begin(), s.end(), ::isdigit);
}

http://ideone.com/OjVJWh


1
这是最好的答案。
马丁·布罗德赫斯特

如果字符串中包含utf8字符,则会出现运行时错误。
狮子王

29

您可以使用boost :: lexical_cast以C ++的方式进行操作。如果您真的坚持不使用增强功能,则只需检查其功能即可。很简单

try 
{
  double x = boost::lexical_cast<double>(str); // double could be anything with >> operator.
}
catch(...) { oops, not a number }

21
使用try{} catch{}一个好主意?我们不应该尽可能避免吗?
Nawaz


14
在这里尝试try {} catch {}是合适的。但是,catch(...)只是不好的做法。在这种情况下,请对您的异常处理程序使用boost :: bad_lexical_cast。
NuSkooler

5
我觉得这是试图从文件中读取。无论您对文件进行多少检查,您都不会知道是否可以从文件中读取文件。它要么起作用,要么不起作用。在这种情况下,您将需要捕获异常。因此,在这种情况下,我认为这是一种完全好的方法。
Casey

4
@EarlGray-我当然很想听听Windows将采取哪些与OS相关的操作。该标准非常清楚此代码的行为方式。
爱德华·斯特兰奇

16

我只是想提出使用迭代的想法,但是其他一些代码也可以进行迭代:

#include <string.h>

bool is_number(const std::string& s)
{
    return( strspn( s.c_str(), "-.0123456789" ) == s.size() );
}

它不像检查小数点或减号时那样健壮,因为它允许在任何位置出现多个或多个。好处是,这是一行代码,不需要第三方库。

取出“。” 和“-”(如果允许所有正整数)。


错误:未在此范围内声明“ strspn”,我认为这是因为我错过了“ #include”,但错过了一个
Qwertie 2014年

4
如果要使用std::string,请使用其find_first_not_of成员函数。
Ben Voigt 2014年

5
如果您输入的字符串“ 12.3-4.55-”显然不是有效数字,则此操作将失败
Buzzrick

Buzzrick,建议的答案已经指出,使用您提到的非数字会失败。
David Rector

如果您仅将其限制为“ 0123456789”,则该公式非常适合测试无符号整数
没有特别的

16

我建议使用正则表达式。完整的正则表达式匹配项(例如,使用boost :: regex

-?[0-9]+([\.][0-9]+)?

将显示字符串是否为数字。这包括正数和负数,整数和十进制。

其他变化:

[0-9]+([\.][0-9]+)?

(仅正面)

-?[0-9]+

(仅整数)

[0-9]+

(仅正整数)


哎呀,我试图std::regex与gcc 4.7,gcc 4.8 一起使用-它们都在regexp中抛出std::regex_error任何迹象[,即使是无辜的“ [abc]”(我做错了吗?)。clang-3.4根本不知道<regex>。无论如何,这似乎是最明智的答案,+ 1。
Dmytro Sirenko

3
@EarlGray:Regex仅可从GCC 4.9
轨道上的Lightness Races

13

这是使用<regex>库的另一种方法:

bool is_integer(const std::string & s){
    return std::regex_match(s, std::regex("[(-|+)|][0-9]+"));
}

嗯,会的。我已经更新了更好的解决方案。谢谢。
mpataki14 2014年

它不应该是“ [(-| +)|] [0-9] +”(加号而不是星号),否则您的正则表达式可以匹配“-”或“ +”作为有效数字。
大卫·穆尔德

真好 我不确定(,|和)在第一个字符类中的作用-据我所知,那些元字符在字符类中失去了特殊的含义。“ ^ [-+]?[0-9] + $”怎么样?
U007D

它可能效率不高。每次调用此函数时,它都会调用std :: regex构造函数,该构造函数将编译regex。
user31264 '19

12

使用此解决方案,您可以检查从负数到正数甚至浮点数的所有内容。当您将的类型更改num为整数时,如果字符串中包含点,则会出现错误。

#include<iostream>
#include<sstream>
using namespace std;


int main()
{
      string s;

      cin >> s;

      stringstream ss;
      ss << s;

      float num = 0;

      ss >> num;

      if(ss.good()) {
          cerr << "No Valid Number" << endl;
      }
      else if(num == 0 && s[0] != '0') {
          cerr << "No Valid Number" << endl;
      }
      else {
          cout << num<< endl;
      }             
}

证明:C ++程序


10

我发现以下代码是最可靠的(c ++ 11)。它同时捕获整数和浮点数。

#include <regex>
bool isNumber( std::string token )
{
    return std::regex_match( token, std::regex( ( "((\\+|-)?[[:digit:]]+)(\\.(([[:digit:]]+)?))?" ) ) );
}

似乎该行using namespace std;是不必要的。
Xam

5

这是检查正整数的解决方案:

bool isPositiveInteger(const std::string& s)
{
    return !s.empty() && 
           (std::count_if(s.begin(), s.end(), std::isdigit) == s.size());
}

5

试试这个:

isNumber(const std::string &str) {    
  return !str.empty() && str.find_first_not_of("0123456789") == string::npos;
}

1
这仅测试无符号整数
没有特别之处

4

布伦丹·本

bool isNumber(string line) 
{
    return (atoi(line.c_str())); 
}

几乎可以。

假设任何以0开头的字符串都是数字,只需对此情况添加检查

bool isNumber(const string &line) 
{
 if (line[0] == '0') return true;
 return (atoi(line.c_str()));
}

ofc“ 123hello”将返回true,就像Tony D指出的那样。



3

我的解决方案使用C ++ 11正则表达式(#include <regex>),可用于更精确的检查,例如unsigned intdouble等等:

static const std::regex INT_TYPE("[+-]?[0-9]+");
static const std::regex UNSIGNED_INT_TYPE("[+]?[0-9]+");
static const std::regex DOUBLE_TYPE("[+-]?[0-9]+[.]?[0-9]+");
static const std::regex UNSIGNED_DOUBLE_TYPE("[+]?[0-9]+[.]?[0-9]+");

bool isIntegerType(const std::string& str_)
{
  return std::regex_match(str_, INT_TYPE);
}

bool isUnsignedIntegerType(const std::string& str_)
{
  return std::regex_match(str_, UNSIGNED_INT_TYPE);
}

bool isDoubleType(const std::string& str_)
{
  return std::regex_match(str_, DOUBLE_TYPE);
}

bool isUnsignedDoubleType(const std::string& str_)
{
  return std::regex_match(str_, UNSIGNED_DOUBLE_TYPE);
}

您可以在http://ideone.com/lyDtfi上找到此代码,可以轻松对其进行修改以满足要求。


我要求下选民帮助我理解问题,我将改善答案。谢谢。
aniliitb10

2

基于kbjorklu的评论的解决方案是:

bool isNumber(const std::string& s)
{
   return !s.empty() && s.find_first_not_of("-.0123456789") == std::string::npos;
}

David Rector的答案一样,它对于具有多个点或减号的字符串并不稳健,但是您可以删除这些字符以检查整数。


但是,我偏爱基于Ben Voigt解决方案的解决方案,它strtod在cstdlib中使用以查找十进制值,科学/工程表示法,十六进制表示法(C ++ 11)甚至是INF / INFINITY / NAN(C ++ 11)是:

bool isNumberC(const std::string& s)
{
    char* p;
    strtod(s.c_str(), &p);
    return *p == 0;
}

2

我们可以使用stringstream 类。

    bool isNumeric(string str)
    {
       stringstream stream;                   
       double number;

       stream<<str;
       stream>>number;

       return stream.eof();
    }

2

使用<regex>。此代码已经过测试!

bool isNumber(const std::string &token)
{
    return std::regex_match(token, std::regex("(\\+|-)?[0-9]*(\\.?([0-9]+))$"));
}

1

在查阅了更多文档后,我想出了一个可以满足我的需求的答案,但可能对其他人没有帮助。在这里(没有烦人的return true和return false语句:-))

bool isNumber(string line) 
{
    return (atoi(line.c_str())); 
}

4
如果数字恰好是0,您将得到一个假负数。
Charles Salvia的

3
这将返回任何前导数字,并且不会警告您尾随垃圾(例如“ 123hello” ==> 123)。@查尔斯:布伦丹提到他只需要在对另一个答案的评论中认出正整数即可。
托尼·德罗伊

1

我认为这个正则表达式应该可以处理几乎所有情况

"^(\\-|\\+)?[0-9]*(\\.[0-9]+)?"

因此,您可以尝试以下可同时使用的函数(Unicode和ANSI)

bool IsNumber(CString Cs){
Cs.Trim();

#ifdef _UNICODE
std::wstring sr = (LPCWSTR)Cs.GetBuffer(Cs.GetLength());
return std::regex_match(sr, std::wregex(_T("^(\\-|\\+)?[0-9]*(\\.[0-9]+)?")));

#else
    std::string s = (LPCSTR)Cs.GetBuffer();
return std::regex_match(s, std::regex("^(\\-|\\+)?[0-9]*(\\.[0-9]+)?"));
#endif
}

1
include <string>

对于验证双打:

bool validateDouble(const std::string & input) {
int decimals = std::count(input.begin(), input.end(), '.'); // The number of decimals in the string
int negativeSigns = std::count(input.begin(), input.end(), '-'); // The number of negative signs in the string

if (input.size() == decimals + negativeSigns) // Consists of only decimals and negatives or is empty
    return false;
else if (1 < decimals || 1 < negativeSigns) // More than 1 decimal or negative sign
    return false;
else if (1 == negativeSigns && input[0] != '-') // The negative sign (if there is one) is not the first character
    return false;
else if (strspn(input.c_str(), "-.0123456789") != input.size()) // The string contains a character that isn't in "-.0123456789"
    return false;
return true;

}

用于验证整数(带负数)

bool validateInt(const std::string & input) {
int negativeSigns = std::count(input.begin(), input.end(), '-'); // The number of negative signs in the string

if (input.size() == negativeSigns) // Consists of only negatives or is empty
    return false;
else if (1 < negativeSigns) // More than 1 negative sign
    return false;
else if (1 == negativeSigns && input[0] != '-') // The negative sign (if there is one) is not the first character
    return false;
else if (strspn(input.c_str(), "-0123456789") != input.size()) // The string contains a character that isn't in "-0123456789"
    return false;
return true;

}

用于验证无符号整数

bool validateUnsignedInt(const std::string & input) {
return (input.size() != 0 && strspn(input.c_str(), "0123456789") == input.size()); // The string is not empty and contains characters only in "0123456789"

}


1
bool isNumeric(string s){
    if ( !s.empty() && s[0] != '-' )
        s = "0" + s; //prepend 0

    string garbage;

    stringstream ss(s); 
    ss >> *(auto_ptr<double>(new double)) >> garbage;
/*
//the line above extracts the number into an anonymous variable. it could also be done like this:
double x;
ss >> x >> garbage;
*/
    //if there is no garbage return true or else return false
    return garbage.empty(); 
}

工作原理: stringstream >>重载可以将字符串转换为各种算术类型,方法是从stringstream中顺序读取字符(在这种情况下为ss),直到用完字符,或者下一个字符不符合存储条件转换为目标变量类型。

范例1:

stringstream ss("11");
double my_number;
ss >> my_number; //my number = 11

示例2:

stringstream ss("011");
double my_number;
ss >> my_number; //my number = 11

示例3:

stringstream ss("11ABCD");
double my_number;
ss >> my_number; //my number = 11 (even though there are letters after the 11)

“垃圾”变量说明”:

为什么不只检查提取到我的double中的值是否有效,然后返回true呢?

请注意,即使输入字符串为“ 11ABCD”(非数字),上述example3仍将成功将数字11读取到my_number变量中。

为了处理这种情况,我们可以对字符串变量(我称为垃圾)进行另一次提取,该变量可以读取最初提取为double类型的变量后可能保留在字符串缓冲区中的任何内容。如果剩下任何东西,它将被读为“垃圾”,这意味着传入的完整字符串不是数字(它只是以1开头)。在这种情况下,我们要返回false;

前面的“ 0”说明”:

尝试将单个字符提取为双精度字会失败(将0转换为双精度字),但仍会将字符串缓冲区位置移到该字符之后。在这种情况下,我们的垃圾读取将为空,这将导致函数错误地返回true。为了解决这个问题,我在字符串的前面加上了0,这样,例如,如果传入的字符串是“ a”,则将其更改为“ 0a”,以便将0提取为双精度型,而将“ a”提取为垃圾级。

前置0不会影响数字的值,因此该数字仍将正确提取到我们的double变量中。


1
尽管此代码可以回答问题,但提供有关此代码为何和/或如何回答问题的其他上下文,可以改善其长期价值。
Ajean 2015年

1

检查字符串是数字整数还是浮点数,或者可以使用:

 #include <sstream>

    bool isNumber(string str) {
    double d;
    istringstream is(str);
    is >> d;
    return !is.fail() && is.eof();
}

1
对于包含值“ 10_is_not_a_number”的字符串,它将返回10。
KorreyD 2015年

1

还有另一个答案,即使用stold(尽管如果不需要精度,也可以使用stof/ stod)。

bool isNumeric(const std::string& string)
{
    std::size_t pos;
    long double value = 0.0;

    try
    {
        value = std::stold(string, &pos);
    }
    catch(std::invalid_argument&)
    {
        return false;
    }
    catch(std::out_of_range&)
    {
        return false;
    }

    return pos == string.size() && !std::isnan(value);
}


1

试试这个:

bool checkDigit(string str)
{  
   int n=str.length();

   for(int i=0;    i   < n ;   i++)
   {
     if(str[i]<'0' || str[i]>'9')
       return false;
   }

   return true;
}

1

您可以使用boost :: lexical_cast测试字符串是否可以转换为整数。如果抛出bad_lexical_cast异常,则无法转换字符串,否则可以转换。

请参阅以下此类测试程序的示例:

#include <boost/lexical_cast.hpp>
#include <iostream>

int main(int, char** argv)
{
        try
        {
                int x = boost::lexical_cast<int>(argv[1]);
                std::cout << x << " YES\n";
        }
        catch (boost::bad_lexical_cast const &)
        {
                std:: cout << "NO\n";
        }
        return 0;
}

示例执行:

# ./a.out 12
12 YES
# ./a.out 12/3
NO

0

几个月前,我实现了一种确定字符串是否为整数,十六进制或双精度的方法。

enum{
        STRING_IS_INVALID_NUMBER=0,
        STRING_IS_HEXA,
        STRING_IS_INT,
        STRING_IS_DOUBLE
};

bool isDigit(char c){
    return (('0' <= c) && (c<='9'));
}

bool isHexaDigit(char c){
    return ((('0' <= c) && (c<='9')) || ((tolower(c)<='a')&&(tolower(c)<='f')));
}


char *ADVANCE_DIGITS(char *aux_p){

    while(CString::isDigit(*aux_p)) aux_p++;
    return aux_p;
}

char *ADVANCE_HEXADIGITS(char *aux_p){

    while(CString::isHexaDigit(*aux_p)) aux_p++;
    return aux_p;
}


int isNumber(const string & test_str_number){
    bool isHexa=false;
    char *str = (char *)test_str_number.c_str();

    switch(*str){
    case '-': str++; // is negative number ...
               break;
    case '0': 
              if(tolower(*str+1)=='x')  {
                  isHexa = true;
                  str+=2;
              }
              break;
    default:
            break;
    };

    char *start_str = str; // saves start position...
    if(isHexa) { // candidate to hexa ...
        str = ADVANCE_HEXADIGITS(str);
        if(str == start_str)
            return STRING_IS_INVALID_NUMBER;

        if(*str == ' ' || *str == 0) 
            return STRING_IS_HEXA;

    }else{ // test if integer or float
        str = ADVANCE_DIGITS(str);
        if(*str=='.') { // is candidate to double
            str++;
            str = ADVANCE_DIGITS(str);
            if(*str == ' ' || *str == 0)
                return STRING_IS_DOUBLE;

            return STRING_IS_INVALID_NUMBER;
        }

        if(*str == ' ' || *str == 0)
            return STRING_IS_INT;

    }

    return STRING_IS_INVALID_NUMBER;


}

然后,如果您执行以下操作,则可以轻松地在程序中将数字转换为其类型的函数,

string val; // the string to check if number...

switch(isNumber(val)){
   case STRING_IS_HEXA: 
   // use strtol(val.c_str(), NULL, 16); to convert it into conventional hexadecimal
   break;
   case STRING_IS_INT: 
   // use (int)strtol(val.c_str(), NULL, 10); to convert it into conventional integer
   break;
   case STRING_IS_DOUBLE:
   // use atof(val.c_str()); to convert it into conventional float/double
   break;
}

您可以意识到,如果未检测到数字,该函数将返回0。可以将0视为假(如布尔值)。


0

我提出一个简单的约定:

如果到ASCII的转换> 0或以0开头,则为数字。它不是完美的但很快。

像这样:

string token0;

if (atoi(token0.c_str())>0 || isdigit(token0.c_str()[0]) ) { //this is a value
    // do what you need to do...
}

0

此功能可处理所有可能的情况:

bool AppUtilities::checkStringIsNumber(std::string s){
    //Eliminate obvious irritants that could spoil the party
    //Handle special cases here, e.g. return true for "+", "-", "" if they are acceptable as numbers to you
    if (s == "" || s == "." || s == "+" || s == "-" || s == "+." || s == "-.") return false;

    //Remove leading / trailing spaces **IF** they are acceptable to you
    while (s.size() > 0 && s[0] == ' ') s = s.substr(1, s.size() - 1);
    while (s.size() > 0 && s[s.size() - 1] == ' ') s = s.substr(0, s.size() - 1);


    //Remove any leading + or - sign
    if (s[0] == '+' || s[0] == '-')
        s = s.substr(1, s.size() - 1);

    //Remove decimal points
    long prevLength = s.size();

    size_t start_pos = 0;
    while((start_pos = s.find(".", start_pos)) != std::string::npos) 
        s.replace(start_pos, 1, "");

    //If the string had more than 2 decimal points, return false.
    if (prevLength > s.size() + 1) return false;

    //Check that you are left with numbers only!!
    //Courtesy selected answer by Charles Salvia above
    std::string::const_iterator it = s.begin();
    while (it != s.end() && std::isdigit(*it)) ++it;
    return !s.empty() && it == s.end();

    //Tada....
}

0

您可以简单地使用sscanf的返回码来确定它是否为int吗?

bool is_number(const std::string& s)
{
    int value;
    int result = sscanf(valueStr.c_str(), "%d", &value);
    return (result != EOF && readResult != 0);
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.