Как определить, является ли строка числом с C++?

У меня было довольно много проблем с написанием функции, которая проверяет, является ли строка числом. Для игры, которую я пишу, мне просто нужно проверить, является ли строка из файла, который я читаю, числом или нет (я буду знать, является ли это параметром таким образом). Я написал ниже функцию, которая, как я считаю, работает гладко (или я случайно отредактировал, чтобы остановить ее, или я шизофреник или Windows шизофреник):

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

27 ответов


наиболее эффективным способом было бы просто перебирать струны, пока вы не найдете нецифры. Если есть какие-либо незначные символы, вы можете считать строку не числом.

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(), [](char c) { return !std::isdigit(c); }) == s.end();
}

Как указано в комментариях ниже, это работает только для положительных целых чисел. Если вам нужно обнаружить отрицательные целые числа или дроби, вы должны пойти с более мощной библиотеки-решение. Хотя, добавляя поддержка отрицательных целых чисел довольно тривиальна.


зачем изобретать велосипед? Стандартная библиотека 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;
}

вы можете сделать это способом C++ с boost:: lexical_cast. Если вы действительно настаиваете на том, чтобы не использовать boost, вы можете просто изучить, что он делает, и сделать это. Все очень просто.

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

С помощью этого решения вы можете проверить все от отрицательных до положительных чисел и даже чисел float. При изменении типа num to integer вы получите ошибку, если строка содержит точку.

#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++


С компилятором 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


Я просто хотел бросить эту идею, которая использует итерацию, но какой-то другой код делает эту итерацию:

#include <string.h>

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

он не является надежным, как это должно быть при проверке десятичной точки или знака минуса, поскольку он позволяет быть более одного из каждого и в любом месте. Хорошо, что это одна строка кода и не требует сторонней библиотеки.

вынуть '."и" - " если положительные целые числа-это все, что разрешено.


Я бы предложил подход с регулярным выражением. Полное регулярное выражение (например, с помощью boost:: regex) С

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

покажет, является ли строка числом или нет. Это включает в себя положительные и отрицательные числа, целое число как десятичное.

другие изменения:

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

(только положительные)

-?[0-9]+

(только целое число)

[0-9]+

(только положительное число)


вот еще один способ сделать это с помощью <regex> библиотека:

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

Я нашел следующий код самым надежным (c++11). Он ловит оба целых числа и плавает.

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

попробуйте это:

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

вот решение для проверки целых положительных чисел:

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

Брендан этой

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, как отметил Тони Д.


самый простой, который я могу придумать в C++

bool isNumber(string s) {
    if(s.size()==0) return false;
    for(int i=0;i<s.size();i++) {
        if((s[i]>='0' && s[i]<='9')==false) {
            return false;
        }
    }
    return true;
}

пример рабочего кода:https://ideone.com/nRX51Y


решение на основе комментарий kbjorklu - это:

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

С ответ Дэвида ректора он не надежен для строк с несколькими точками или знаками минус, но вы можете удалить эти символы, чтобы просто проверить целые числа.


однако я неравнодушен к решению, основанному на решение Бена Фойта, используя 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;
}

Я думаю, что это регулярное выражение должно обрабатывать почти всех случаях

"^(\-|\+)?[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
}

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;

}

Для Проверки Ints (С Негативами)

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;

}

Для Проверки Неподписанных Ints

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"

}


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 >> overload может преобразовывать строки в различные арифметические типы он делает это, последовательно считывая символы из 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

example3:

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

"мусор" переменная объяснение":

почему бы просто не проверить, имеет ли извлечение в мой double допустимое значение, а затем вернуть true, если это так?

обратите внимание, что example3 выше по-прежнему успешно считывает число 11 в переменную my_number, даже если входная строка "11ABCD" (которая не является числом).

для обработки этого случая мы можем сделать другое извлечение в строковую переменную (которую я назвал мусором), которая может читать все, что может быть оставлено в строковом буфере после первоначального извлечения в переменную типа double. Если что-то останется, оно будет прочитано в "мусор", что означает, что полная строка, переданная, не была числом (она начинается с единицы). в этом случае мы хотели бы вернуть false;

прилагаемое" 0 "объяснение":

попытка извлечь один символ в double завершится неудачей(возврат 0 в наш double), но все равно переместит позицию буфера строки вслед за персонажем. В этом случае наше чтение мусора будет пустым, что приведет к неправильному возвращению функции true. чтобы обойти это, я добавил 0 к строке, так что если, например, переданная строка была "a", она изменяется на "0a", так что 0 будет извлечен в double и "a" будет извлечен в мусор.

добавление 0 не повлияет на значение числа, поэтому число все равно будет правильно извлечено в нашу двойную переменную.


чтобы проверить, является ли строка целым числом или плавающей запятой, или вы можете использовать:

 #include <sstream>

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

мое решение с использованием C++11 regex (#include <regex>), его можно использовать для более точной проверки, как unsigned int, double etc:

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, это можно легко доработать для того чтобы соотвествовать.


Как мне открылось вответ на мой вопрос, я чувствую, что вы должны использовать boost::conversion:: try_lexical_convert


проконсультировавшись с документацией немного больше, я придумал ответ, который поддерживает мои потребности, но, вероятно, не будет так полезен для других. Вот он (без раздражающего возврата истинных и возврата ложных утверждений : -))

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

несколько месяцев назад, я реализовал способ определить, если строка является числом, шестнадцатеричном или двойной.

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 можно рассматривать как false (например, boolean).


Я предлагаю простое соглашение:

если преобразование в 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...
}

еще один ответ, который использует 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);
}

мы можем использовать stringstream класса.

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

       stream<<str;
       stream>>number;

       return stream.eof();
    }

используя <regex>. Этот код был протестирован!

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

эта функция заботится обо всех возможных случаях:

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....
}