【问题标题】:How do I check if a C++ string is an int?如何检查 C++ 字符串是否为 int?
【发布时间】:2011-02-20 03:36:15
【问题描述】:

当我使用getline 时,我会输入一堆字符串或数字,但我只希望while 循环输出不是数字的“单词”。 那么有没有办法检查“单词”是否是一个数字?我知道我可以使用atoi() C-strings 但是对于 string 类的字符串呢?

int main () {
  stringstream ss (stringstream::in | stringstream::out);
  string word;
  string str;
  getline(cin,str);
  ss<<str;
  while(ss>>word)
    {
      //if(    )
        cout<<word<<endl;
    }
}

【问题讨论】:

标签: c++ string types stringstream


【解决方案1】:

另一个版本...

使用strtol,将其包装在一个简单的函数中以隐藏其复杂性:

inline bool isInteger(const std::string & s)
{
   if(s.empty() || ((!isdigit(s[0])) && (s[0] != '-') && (s[0] != '+'))) return false;

   char * p;
   strtol(s.c_str(), &p, 10);

   return (*p == 0);
}

为什么是strtol

就我喜欢 C++ 而言,有时 C API 是我所关心的最佳答案:

  • 对于授权失败的测试来说,使用异常是多余的
  • 当 C 标准库有一个鲜为人知的专用函数来完成这项工作时,词法转换创建的临时流对象是过度杀伤和效率过低的。

它是如何工作的?

strtol 乍一看似乎很原始,因此解释将使代码更易于阅读:

strtol 将解析字符串,在第一个不能被视为整数一部分的字符处停止。如果您提供p(正如我在上面所做的那样),它将在第一个非整数字符处设置p

我的推理是,如果p没有设置到字符串的末尾(0字符),那么字符串s中有一个非整数字符,意思是s不是一个正确的整数.

第一个测试是为了消除极端情况(前导空格、空字符串等)。

当然,该功能应该根据您的需要进行定制(前导空格是错误的吗?等等)。

来源:

请参阅strtol 的描述:http://en.cppreference.com/w/cpp/string/byte/strtol

还可以查看strtol 的姐妹函数(strtodstrtoul 等)的描述。

【讨论】:

  • +1,到目前为止最好的答案。 lexical_cast 是矫枉过正的,它接近于不好的做法。
  • @David Titarenco : lexical_cast is overkill and it borders on bad practice : 仅在不小心使用时,对于任何其他功能也是如此。 boost::lexical_cast 的真正用途是用于泛型代码,或者快速而肮脏的代码。 通用代码:当你有一个未知类型T时,使用开关来使用strtol、strtol等是错误的。使用 boost::lexical_cast 是正确的,为正确的类型提供 boost::lexical_cast 特化以有效使用。 Quick'n'dirty code:如果该代码不需要速度,那么使用 boost::lexical_cast 是最简单的方法。
【解决方案2】:

如果输入是数字加文本,则接受的答案将给出误报,因为“stol”将转换第一个数字并忽略其余数字。

我最喜欢下面的版本,因为它是一个很好的单行代码,不需要定义函数,你可以在需要的地方复制和粘贴。

#include <string>

...

std::string s;

bool has_only_digits = (s.find_first_not_of( "0123456789" ) == std::string::npos);

编辑:如果你喜欢这个实现,但你确实想将它用作函数,那么应该这样做:

bool has_only_digits(const string s){
  return s.find_first_not_of( "0123456789" ) == string::npos;
}

【讨论】:

  • “它是一个不错的单行代码,不需要定义函数,你可以在需要的地方复制和粘贴”,但你应该定义一个函数。请不要复制粘贴代码。
  • @FerdinandoRandisi 负数呢?
  • 没错,这不适用于负数。但是您可以检查第一个字符是 + 还是 -,如果是这种情况,只需传递字符串的其余部分。
  • 哇,这是一个很好的答案!没想到
  • @Deqing 是真的。如果您碰巧修改它以使其适用于正整数和负整数,我很乐意编辑我的答案。
【解决方案3】:

你可以试试boost::lexical_cast。如果失败,它会抛出 bad_lexical_cast 异常。

在你的情况下:

int number;
try
{
  number = boost::lexical_cast<int>(word);
}
catch(boost::bad_lexical_cast& e)
{
  std::cout << word << "isn't a number" << std::endl;
}

【讨论】:

  • 在字符串解析代码中抛出和捕获异常只会让一切都慢下来。
【解决方案4】:

如果您只是检查 word 是否是一个数字,那并不难:

#include <ctype.h>

...

string word;
bool isNumber = true;
for(string::const_iterator k = word.begin(); k != word.end(); ++k)
    isNumber &&= isdigit(*k);

根据需要进行优化。

【讨论】:

    【解决方案5】:

    使用全能的 C stdio/string 函数:

    int dummy_int;
    int scan_value = std::sscanf( some_string.c_str(), "%d", &dummy_int);
    
    if (scan_value == 0)
        // does not start with integer
    else
        // starts with integer
    

    【讨论】:

    • 如果sscanf 返回匹配项的数量,那么您的示例(cmets)是错误的。还是我误解了什么?
    【解决方案6】:

    您可以按照建议使用boost::lexical_cast,但如果您对字符串有任何先验知识(即,如果字符串包含整数文字,它将没有任何前导空格,或者整数永远不会用指数写入),那么滚动你自己的函数应该既更有效,也不是特别困难。

    【讨论】:

      【解决方案7】:

      好吧,我认为你有 3 个选项。

      1:如果您只是想检查数字是否为整数,而不关心转换它,而只是希望将其保留为字符串并且不关心潜在的溢出,请检查它是否匹配整数的正则表达式在这里是理想的。

      2:您可以使用 boost::lexical_cast 然后捕获一个潜在的 boost::bad_lexical_cast 异常来查看转换是否失败。如果您可以使用 boost 并且转换失败是一种例外情况,这将很有效。

      3:滚动您自己的函数,类似于 lexical_cast,它检查转换并根据转换是否成功返回真/假。如果 1 和 2 不符合您的要求,这将起作用。

      【讨论】:

      • Pick A. Regex 不会告诉你它是否超出了整数的范围。
      • 原始问题没有提到想要检查溢出,甚至想要使用数字作为整数。据我所知,他们计划将数字保存在字符串中以避免此类限制。
      • 好的,我尝试更改回复以包含我最初列出的 3 个选项,每个选项都有一个原因。
      【解决方案8】:

      这是另一种解决方案。

      try
      {
        (void) std::stoi(myString); //cast to void to ignore the return value   
        //Success! myString contained an integer
      } 
      catch (const std::logic_error &e)
      {   
        //Failure! myString did not contain an integer
      }
      

      【讨论】:

      • 异常不应用于正常操作和流量控制。它们应该只用于......你猜对了:例外。 ;)
      • 感谢您的反馈!
      • 警告:无论如何它不会工作,std::stoi 不会抛出,它会在失败的情况下返回 0。
      【解决方案9】:

      从 C++11 开始,您可以使用 std::all_of::isdigit

      #include <algorithm>
      #include <cctype>
      #include <iostream>
      #include <string_view>
      
      int main([[maybe_unused]] int argc, [[maybe_unused]] char *argv[])
      {
          auto isInt = [](std::string_view str) -> bool {
              return std::all_of(str.cbegin(), str.cend(), ::isdigit);
          };
      
          for(auto &test : {"abc", "123abc", "123.0", "+123", "-123", "123"}) {
              std::cout << "Is '" << test << "' numeric? " 
                  << (isInt(test) ? "true" : "false") << std::endl;
          }
      
          return 0;
      }
      

      使用Godbolt查看结果。

      【讨论】:

        【解决方案10】:
        template <typename T>
        const T to(const string& sval)
        {
                T val;
                stringstream ss;
                ss << sval;
                ss >> val;
                if(ss.fail())
                        throw runtime_error((string)typeid(T).name() + " type wanted: " + sval);
                return val;
        }
        

        然后你可以这样使用它:

        double d = to<double>("4.3");
        

        int i = to<int>("4123");
        

        【讨论】:

        • 我宁愿使用 boost::lexical_cast。
        • @SebastianMach 原始示例也是如此。
        【解决方案11】:

        我已修改paercebal's method 以满足我的需要:

        typedef std::string String;    
        
        bool isInt(const String& s, int base){
           if(s.empty() || std::isspace(s[0])) return false ;
           char * p ;
           strtol(s.c_str(), &p, base) ;
           return (*p == 0) ;
        }
        
        
        bool isPositiveInt(const String& s, int base){
           if(s.empty() || std::isspace(s[0]) || s[0]=='-') return false ;
           char * p ;
           strtol(s.c_str(), &p, base) ;
           return (*p == 0) ;
        }
        
        
        bool isNegativeInt(const String& s, int base){
           if(s.empty() || std::isspace(s[0]) || s[0]!='-') return false ;
           char * p ;
           strtol(s.c_str(), &p, base) ;
           return (*p == 0) ;
        }
        

        注意:

        1. 您可以检查各种基数(二进制、八进制、十六进制等)
        2. 确保确保您没有将1、负值或&gt;36 值作为基数传递。
        3. 如果您将0 作为基数传递,它将自动检测基数,即以0x 开头的字符串将被视为十六进制,以0 开头的字符串将被视为八进制。字符不区分大小写。
        4. 字符串中的任何空格都会使其返回 false。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-04-30
          • 1970-01-01
          • 1970-01-01
          • 2013-03-07
          • 1970-01-01
          • 2015-10-25
          • 2016-03-29
          • 1970-01-01
          相关资源
          最近更新 更多