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


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

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

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 }

С помощью этого решения вы можете проверить все от отрицательных до положительных чисел и даже чисел с плавающей точкой. При изменении типа 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++

с компилятором 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()));
}

ОФК "123hello"вернется истинным, как отметил Тони Д.

самое простое, что я могу придумать в 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 >> может преобразовывать строки в различные арифметические типы это делается путем последовательного считывания символов из stringstream (в данном случае ss) до тех пор, пока не закончатся символы или следующий символ не будет соответствовать критериям, которые будут сохранены в типе переменной назначения.

Пример 1:

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

example2:

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)

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

почему бы просто не проверить, имеет ли извлечение в мой двойник действительное значение, а затем вернуть 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 (#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....
}