【问题标题】:Convert String containing several numbers into integers将包含多个数字的字符串转换为整数
【发布时间】:2010-11-22 05:03:27
【问题描述】:

我知道这个问题过去可能已经被问过好几次了,但无论如何我都会继续。

我有一个程序要从键盘输入中获取一串数字。数字将始终采用“66 33 9”的形式本质上,每个数字都用空格分隔,用户输入的数字将始终包含不同数量的数字。

我知道,如果每个用户输入的字符串中的数字数量是恒定的,则使用“sscanf”会起作用,但对我来说并非如此。另外,因为我是 C++ 新手,所以我更喜欢处理“字符串”变量而不是字符数组。

【问题讨论】:

  • IMO 通常更喜欢 std::string 而不是原始字符缓冲区并不是“新手”的标志,而是成熟的标志。

标签: c++ string integer


【解决方案1】:

我假设您想读取整行,并将其解析为输入。所以,首先抓住这条线:

std::string input;
std::getline(std::cin, input);

现在把它放在stringstream

std::stringstream stream(input);

并解析

while(1) {
   int n;
   stream >> n;
   if(!stream)
      break;
   std::cout << "Found integer: " << n << "\n";
}

记得加入

#include <string>
#include <sstream>

【讨论】:

  • 你的意思是解析:int n; while(stream &gt;&gt; n) {std::cout &lt;&lt; "Found integer: " &lt;&lt; n &lt;&lt; "\n";}?干净多了
  • 甚至解析:for (int n; stream &gt;&gt; n;) {std::cout &lt;&lt; "Found integer: " &lt;&lt; n &lt;&lt; "\n";}
【解决方案2】:

C++ String Toolkit Library (Strtk) 对您的问题有以下解决方案:

#include <iostream>
#include <string>
#include <deque>
#include <algorithm>
#include <iterator>

#include "strtk.hpp"

int main()
{
   std::string s = "1 23 456 7890";

   std::deque<int> int_list;
   strtk::parse(s," ",int_list);

   std::copy(int_list.begin(),
             int_list.end(),
             std::ostream_iterator<int>(std::cout,"\t"));

   return 0;
}

更多例子可以在Here找到

【讨论】:

    【解决方案3】:
    #include <string>
    #include <vector>
    #include <iterator>
    #include <sstream>
    #include <iostream>
    
    int main() {
       std::string input;
       while ( std::getline( std::cin, input ) )
       {
          std::vector<int> inputs;
          std::istringstream in( input );
          std::copy( std::istream_iterator<int>( in ), std::istream_iterator<int>(),
             std::back_inserter( inputs ) );
    
          // Log process: 
          std::cout << "Read " << inputs.size() << " integers from string '" 
             << input << "'" << std::endl;
          std::cout << "\tvalues: ";
          std::copy( inputs.begin(), inputs.end(), 
             std::ostream_iterator<int>( std::cout, " " ) );
          std::cout << std::endl;
       }
     }
    

    【讨论】:

    • +1 将 1000 个 "std::"s 替换为单个 "using namespace std;"并提及您需要的标题。
    • +1,只要您保留 std:: 前缀。它们的可读性是主观的(我习惯了它们,发现它更容易阅读),但通过完全限定名称增强的清晰度是客观的。
    • @j_random_hacker:我实际上并不担心命名空间污染。但是,使每个名称都完全限定可以更容易地查看它的来源。 (“哦,这是一个专有的vectortemplate!”)很多年前,在我当时从事的一个项目中,我们决定完全限定所有名称。那时看起来很热闹,但在大约两周内就变得正常了。我一直站在争论的双方,永远不会回去。可读性是一个主观问题,可以通过使用来改变。清晰是客观的。
    • 通常错误消息由编译器处理,编译器完全限定类型。对于 std:: 命名空间,我通常是明确的。事实上,我只在少数情况下使用 using 指令,在某些情况下会创建短别名。完全限定的优点是对普通读者来说更容易,“查找”、“复制”等名称可以是成员/免费功能的常用名称。不经意的读者会直接知道符号何时是 STL 的一部分。再说一次,除了必须对答案进行更多编辑之外,我没有那么大的动力不去改变它:P
    • @j_random_hacker:我希望看到我的 typedef 名称 bla_foo_bar_data 而不是 std::vector&lt;std::vector&lt;int, std::allocator&lt;int&gt; &gt;, std::allocator&lt;std::vector&lt;int, std::allocator&lt;int&gt; &gt; &gt; - 最好有一个选项将其扩展为完全限定的标识符,但具有所有标准模板参数省略。正如我所说:可读性是一个主观的东西,我相信大多数程序员可以在几周内习惯任何一种方式(并且更喜欢那种方式)。我曾经在你们的营地里,但在两周内学会了喜欢完全限定的名字。从这个角度来看,我不会回去。
    【解决方案4】:
    #include <string>
    #include <vector>
    #include <sstream>
    #include <iostream>
    using namespace std;
    
    int ReadNumbers( const string & s, vector <int> & v ) {
        istringstream is( s );
        int n;
        while( is >> n ) {
            v.push_back( n );
        }
        return v.size();
    }
    
    int main() {
        string s;
        vector <int> v;
        getline( cin, s );
        ReadNumbers( s, v );
        for ( int i = 0; i < v.size(); i++ ) {
            cout << "number is " <<  v[i] << endl;
        }
    }
    

    【讨论】:

      【解决方案5】:
      // get string
      std::string input_str;
      std::getline( std::cin, input_str );
      
      // convert to a stream
      std::stringstream in( input_str );
      
      // convert to vector of ints
      std::vector<int> ints;
      copy( std::istream_iterator<int, char>(in), std::istream_iterator<int, char>(), back_inserter( ints ) );
      

      【讨论】:

      • 或者使用直接初始化:std::vector&lt;int&gt; ints{std::istream_iterator&lt;int, char&gt;{in}, std::istream_iterator&lt;int, char&gt;{}};
      【解决方案6】:

      Here is 如何沿空格将字符串拆分为字符串。然后你可以一个一个地处理它们。

      【讨论】:

        【解决方案7】:

        无符号值的通用解决方案(处理前缀'-'需要一个额外的布尔值):

        template<typename InIter, typename OutIter>
        void ConvertNumbers(InIter begin, InIter end, OutIter out)
        {
            typename OutIter::value_type accum = 0;
            for(; begin != end; ++begin)
            {
                typename InIter::value_type c = *begin;
                if (c==' ') {
                    *out++ = accum; accum = 0; break;
                } else if (c>='0' && c <='9') {
                    accum *= 10; accum += c-'0';
                }
            }
            *out++ = accum;
               // Dealing with the last number is slightly complicated because it
               // could be considered wrong for "1 2 " (produces 1 2 0) but that's similar
               // to "1  2" which produces 1 0 2. For either case, determine if that worries
               // you. If so: Add an extra bool for state, which is set by the first digit,
               // reset by space, and tested before doing *out++=accum.
        }
        

        【讨论】:

        • +1,因为您回答了上述问题(“整数”),但我认为这实际上是在某些方面向不太通用的方向迈出的一步,因为它显然只适用于整数和不是其他类型(IOW:“typename OutIter::value_type accum = 0;”的“通用性”被夸大了)。例如,您将如何处理浮点数?如果您想编写另一个迷你词法分析器,请不要忘记处理科学记数法和 Inf、NaN 等。
        • 好吧,它会接受short[]std::vector&lt;int&gt;std::list&lt;long&gt; 或者实际上任何其他整数类型(包括实现定义的类型)。 Plain 0 将转换为所有这些。我在那里使用 valuetype 的原因是,当用户传递为 __int128[] 和带有这么大数字的字符串时,我不会意外溢出 int
        • 使用 value_type 作为 accum 绝对是正确的方法,但我想我不明白为什么你不能只使用 istringstream 而不是你自己手工制作的词法分析器。然后,您可以使用 operator
        【解决方案8】:

        先尝试strtoken将字符串分开,然后你会处理每个字符串。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-09-24
          • 1970-01-01
          • 2021-11-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多