Как определить, является ли строка числом с помощью 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;.
ephemient

17
@ephemient Мой стиль - делать то же, что и ты. Но так ли это на самом деле?
Бреннан Винсент

2
Ваш прототип функции кажется неподходящим. Почему бы не использовать bool isParam (const string & line)
MikimotoH

4
Да. У меня есть дурная привычка долго писать код при изучении нового языка. Я новичок в C ++ и более не склонен к «ярлыкам» (или предполагаемым ярлыкам).
Брендан Вайнштейн

57
@ Бреннан Винсент: Да, это большое дело. Это тот же класс ошибок , как if (expr) return expr; else return expr;, if (expr == true), (if expr != false), или if ((expr == true) == true). Все они вносят сложность, которая не приносит пользы писателю, читателю или компилятору кода. Устранение ненужных сложностей - это не ярлык; это ключ к написанию лучшего программного обеспечения.
MSalters

Ответы:


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

8
Также не обрабатывает десятичные числа, например: 1.23
littlecodefarmer758

3
@ Реми Лебо, да, это так. На самом деле это не преобразование строки в int. Он просто определяет, состоит ли строка из числовых цифр. Неважно, какой длины строка.
Чарльз Сальвия

5
Не забудьте включить <string> <algorithm>и <cctype>заставить пример C ++ 11 работать.
kR105 04

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
Эта функция удаляет пустое пространство впереди. Таким образом, вы должны проверить первый символ для isdigit.
chmike

1
@chmike: Исходя из моего понимания вопроса, отказ от ведущих пробелов является правильным поведением ( atoiкак используется в вопросе).
Ben Voigt

1
В вопросе это явно не указывалось, но мое понимание требования «проверяет, является ли строка числом» означает, что вся строка является числом, поэтому без пробелов. Я почувствовал необходимость указать, что ваш ответ в этом отношении отличается от других. Ваш ответ может быть нормальным, если в строке могут быть пробелы перед числом.
chmike 01

1
@BenVoigt Вы говорите, что это pбудет установлено, nullptrесли все strtolбудет успешно, верно? Это не то, что я вижу :(
Джонатан Ми,

2
@JonathanMee: Нет, pбудет указывать на NUL, который завершает строку. Так p != 0и *p == 0.
Ben Voigt

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, вы получите ошибку времени выполнения.
Lion King

29

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

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

21
Используете try{} catch{}хорошую идею? Не следует ли нам избегать этого как можно больше?
Nawaz

32
-1 за злоупотребление try catch ... blogs.msdn.com/b/ericlippert/archive/2008/09/10/…
NoSenseEtAl

14
здесь уместно попробовать {} catch {}. Однако catch (...) - это просто плохая практика. В этом случае используйте boost :: bad_lexical_cast для вашего обработчика исключений.
NuSkooler

5
Я чувствую, что это пытается читать из файла. Независимо от того, сколько вы проверяете файл, вы не узнаете, можно ли из него читать, пока не сделаете это. Это либо сработает, либо нет. В этом случае вам нужно перехватить исключение. Так что в данном случае я думаю, что это совершенно хороший способ сделать это.
Кейси

4
@EarlGray - Мне определенно было бы интересно услышать, какие действия, зависящие от ОС, будут выполнять окна. В стандарте совершенно ясно, как должен вести себя этот код.
Эдвард Стрэндж

16

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

#include <string.h>

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

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

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


ошибка: 'strspn' не был объявлен в этой области, я думаю, это потому, что мне не хватает «#include», но какой именно
Qwertie

4
Если вы собираетесь использовать std::string, используйте его find_first_not_ofфункцию-член.
Бен Фойгт

5
Это не сработает, если вы передадите строку «12.3-4.55-», которая, очевидно, не является действительным числом
Баззрик

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

если вы ограничите его только до «0123456789», тогда формула идеально подходит для проверки целого числа без знака
ни одного специального

16

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

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

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

Другие варианты:

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

(только позитив)

-?[0-9]+

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

[0-9]+

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


Кхм, я пытался использовать std::regexс gcc 4.7, gcc 4.8 - они оба выдают std::regex_errorлюбой знак [в регулярном выражении, даже для невинного "[abc]" (я делаю это неправильно?). clang-3.4 вообще не знает <regex>. В любом случае, это кажется наиболее разумным ответом - +1.
Дмитрий Сиренко

3
@EarlGray: Regex правильно доступен только в GCC 4.9
Гонки

13

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

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

Ах, так бы и было. Я обновился с лучшим решением. Спасибо.
mpataki14

Разве это не должно быть «[(- | +) |] [0-9] +» (плюс вместо звезды), иначе ваше регулярное выражение может соответствовать «-» или «+» как действительному числу.
Дэвид Малдер

Ницца. Я не уверен, что (, | и) делают в этом первом классе символов - эти метасимволы теряют свое особое значение внутри класса символов, насколько мне известно. Как насчет «^ [- +]? [0-9] + $»?
U007D

Это может быть неэффективно. Каждый раз, когда это вызывается, он вызывает конструктор std :: regex, который компилирует регулярное выражение.
user31264

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


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" вернется, как заметил Тони Ди.



3

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

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 07

2

Решение, основанное на комментарии 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;
}

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, вы получите ложноотрицательный результат.
Чарльз Сальвия

3
Это вернет любое начальное число и не предупредит вас о мусоре в конце (например, "123hello" ==> 123). @Charles: Брендан упоминает, что ему нужно только распознавать положительные вставки в комментарии к другому ответу.
Тони Делрой

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;

}

Для проверки 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"

}


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

example1:

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

example3:

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

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

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

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

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

добавленное «0» объяснение:

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

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


1
Хотя этот код может ответить на вопрос, предоставление дополнительного контекста относительно того, почему и / или как этот код отвечает на вопрос, повышает его долгосрочную ценность.
Ajean

1

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

 #include <sstream>

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

1
это вернет 10 для строки, содержащей значение «10_is_not_a_number».
KorreyD

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);
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.