【问题标题】:Extract all numbers from stringstream从字符串流中提取所有数字
【发布时间】:2020-04-10 16:56:14
【问题描述】:

我想读取字符串并提取所有数字。

Input: 5a3 1f a0aaaa f1fg3

Output: 53 1 0 13

我试过这段代码:

string s;
getline(cin, s);
stringstream str_strm(s);

int found;
string temp;

while (!str_strm.eof()) {
    str_strm >> temp;
    if (stringstream(temp) >> found)
    {
        cout << found << endl;
    }
}

但是当找到 5(来自示例)之后,自动开始检查其他字符串。如何提取所有数字?

【问题讨论】:

  • 逐个字符读取并使用isdigit()
  • 顺便说一句,您在错误的位置检查 .eof() 。你是从哪里学来的?阅读stackoverflow.com/q/5605125/4386278
  • 当您接受输入是十六进制时,您的输出会有所不同。

标签: c++


【解决方案1】:

这是一个可能的解决方案 - while 循环用于用空格分隔字符串,然后从子字符串中提取数字。

int main()
{
    stringstream ss("5a3 1f a0aaaa f1fg3");
    string str;
    while (getline(ss, str, ' ') ){     
        str.erase(std::remove_if(str.begin(), str.end(), [](unsigned char c) { return !std::isdigit(c); }), str.end());
        cout << str << " ";
    }
}

【讨论】:

    【解决方案2】:

    您可以读取每个空格分隔的单词,然后像这样删除非数字

    std::string word;
    while (std::cin >> word)
    {
      word.erase(std::remove_if(word.begin(), word.end(), 
                   [](unsigned char c) { return not std::isdigit(c); }), 
                 word.end());
      std::cout << word << " ";
    }
    

    对于5a3 1f a0aaaa f1fg3 的输入,它会打印53 1 0 13

    公认的奇怪的删除范围元素的方法是common idiom

    如果你在一行上输入,你甚至可以完全避免循环

    std::string word;
    std::getline(std::cin, word);
    
    word.erase(std::remove_if(word.begin(), word.end(), 
                   [](unsigned char c) { return not std::isdigit(c) 
                                            and not std::isspace(c); }), 
               word.end());
    
    std::cout << word;
    

    【讨论】:

      【解决方案3】:

      请看这里的超简单示例。 (这篇文章的底部有一个更简单的解决方案)

      它使用现代 C++ 元素和算法。而且只有几行代码。

      #include <iostream>
      #include <string>
      #include <regex>
      #include <iterator>
      #include <algorithm>
      #include <sstream>
      
      int main() {
      
          // Read a string from the console
          if (std::string line{}; std::getline(std::cin, line)) {
      
              // Put the complete line into a std::istringstream
              std::istringstream iss{line};
      
              // Print result
              std::transform(std::istream_iterator<std::string>(iss), {}, std::ostream_iterator<std::string>(std::cout, " "),
                  [](const std::string& s) { return std::regex_replace(s, std::regex{ R"([^\d])" }, ""); });
          }
          return 0;
      }
      

      那么,这里发生了什么。让我们逐条来看。所以,首先:

      if (std::string line{}; std::getline(std::cin, line)) {
      

      这是带有初始化程序的if-语句。如果您在 C++ 参考 here 中查找 if,那么您会看到,我们现在可以在 if 的第一部分中添加一个额外的初始化语句。我们为什么要使用它?因为它是范围界定的附加措施。变量“line”仅在 if 语句的范围内使用。在 if 之外不需要它。从功能上看,和写法一样:

      std::string line{};
      if (std::getline(std::cin, line)) {
      

      但是,“line”在 if 语句之外也是可见的。并且,因为我们要防止外部命名空间的污染,所以我们选择了这种方法。

      接下来是std::getline。这将从输入流中读取完整的一行,因此,从控制台 (std::cin) 并将其放入字符串中。 std::getline 返回对流的引用。该流有一个重载的 bool 运算符,如果有失败(或文件结尾),则返回。因此,if 语句检查输入操作是否有效。顺便一提。应该检查所有 IO 操作,无论它们是否有效。

      好的,现在我们的变量“line”中有完整的用户输入行。

      std::istringstream iss{line};
      

      我们将字符串放入std::istringstream。我们这样做是因为我们想利用 C++“iostream”库。 std::istringstream 的行为与任何其他流一样,例如std::cin,您可以从中提取由空格分隔的值。比如std::cin &gt;&gt; v1 &gt;&gt; v2。这种方法的缺点是,您需要提前知道值的数量或使用动态增长的容器和循环。

      这将 ud 带到了我要解释的下一个构造。您可能听说过“迭代器”。迭代器就像指针,可以指向一系列元素。如果您有std::vector 或任何其他容器,那么您可以使用begin()end() 迭代器迭代std::vector 中的所有元素,而不知道std::vector 中有多少元素,而不知道如何它包含许多元素。

      对于输入流,我们有类似的东西:std::istream_iterator。此迭代器将遍历std::sitringstream 中的元素,并通过重复调用提取器运算符&gt;&gt; 返回其模板参数中给定的变量类型。在我们的例子中,这里是std::string。你可能知道问:直到什么时候?终点在哪里。如果您查看std::istream_operatorconstructor number 1 的描述,那么您将看到默认构造函数Constructs the end-of-stream iterator。并且可以使用空的大括号 {} 初始化程序生成默认构造。所以 {} 是结束迭代器。

      如果我们想从std::istringstream 中读取所有std::strings,那么我们在 std::istream_iterator&lt;std::string&gt;(iss) and {}。所以std::istringstream中的每个字符串。

      好的,接下来,有一个类似的输出,std::ostream_iterator。这将为给定范围内的所有元素调用插入器运算符“std::cout,另外还有一个分隔符字符串,它将附加到输出值。

      好的,接下来:std::transform。顾名思义,它将把begin()end() 迭代器之间的元素范围内的元素转换为另一个范围。因此,它将从std::istringstream 转换如上所示的元素并将它们发送到std::ostream 迭代器。所以,我们读取源值,转换它,然后写入它。

      但是,如何转换。对于转换,我们给出了一个简单的 lambda 函数,它调用了std::regex_replace 函数。这是一个标准函数,用其他字符串数据替换字符串的一部分。并且,将被替换的内容由std::regex 指定。这是一种以某种元语言定义并匹配字符串的指定部分的特殊模式。在我们的例子中,我们使用[^\d],这意味着,不是一个数字。您可以测试正则表达式here。您也可以在这里了解它们。

      现在,大家一起来解释上述解决方案。

      所有这些都可以进一步优化为 2 个语句:

      #include <iostream>
      #include <string>
      #include <regex>
      
      int main() {
      
          // Read a string from the console
          if (std::string line{}; std::getline(std::cin, line)) {
      
              // Remove unnecessary characters
              std::cout << std::regex_replace(line, std::regex{ R"([^\d ])" }, "") << "\n";
          }
          return 0;
      }
      

      我想不出更简单的解决方案。

      如有疑问,请提出。

      【讨论】:

      • 效果很好!但我真的不明白,因为我从小就学编程。有没有简单的方法?
      【解决方案4】:

      您可以使用istream 中的get 来获取每个字符,包括空格,然后使用isdigit 来检查数字字符...

      #include <iostream>
      #include <cctype>
      
      int main()
      {
          char ch;
          std::cin.get(ch);
      
          while (!std::cin.eof())
          {
              if (isdigit(ch) || ch == ' ' || ch == '\n')
              {
                  std::cout << ch;
              }
              std::cin.get(ch);
          }
      
          return 0;
      }
      

      但是,您可以避免在 While 循环的表达式中使用 std::cin.eof(),如下所示...

      #include <iostream>
      #include <cctype>
      
      int main()
      {
          char ch;
      
          while (std::cin.get(ch))
          {
              if (isdigit(ch) || ch == ' ' || ch == '\n')
              {
                  std::cout << ch;
              }
          }
      
          return 0;
      }
      

      【讨论】:

        【解决方案5】:

        正则表达式模式匹配可用于查找输入字符串中的所有数字。

        这是一个查找数字的示例程序:

        // C++ program to find all digits in a string 
        
        #include <bits/stdc++.h> 
        using namespace std; 
        int main() {
            string inputString;
            cout << "Enter the input string: ";
            getline(cin, inputString);
            cout << "Digits found: ";
        
            // Define the regular expression matcher and pattern
            smatch matcher; 
            regex pattern("[[:digit:]]");
        
            while (regex_search(inputString, matcher, pattern)) { 
                // Show the match
                cout << matcher.str(0);
        
                // Continue searching the rest of the string 
                inputString = matcher.suffix().str(); 
            } 
            return 0; 
        } 
        

        输出:

        Enter the input string: sdfh354 eutyt;ljkn756897490uiotureu 587689jkgf 90
        Digits found: 35475689749058768990
        

        这是另一种在字符串中查找数字的方法,不使用正则表达式模式匹配:

        #include <iostream>
        #include <cctype>
        #include <bits/stdc++.h> 
        using namespace std;
        
        int main() {
            string rawInput;
            cout <<"Enter input string: ";
            getline(cin, rawInput);
        
            // Get all words from the input string
            stringstream allWords(rawInput);
        
            // Find and print digits in each word
            string word;
            while(allWords >> word) {
                for(int i = 0; word[i]; i++) {
                    // Print only the numbers in the word
                    if(isdigit(word[i])) {
                        cout<<word[i];
                    }
                }
                cout<<" ";
            }
            cout<<"\n";
            return 0;
        }
        

        输出:

        Enter input string: ghjg45 jsdfj 897897 343yut45 90
        45  897897 34345 90 
        

        【讨论】:

        • 这没有给出预期的输出
        • 该程序实际上根据问题查找给定输入字符串中的所有数字。因此,它确实给出了预期的输出。如果输入是 5a3 1f a0aaaa f1fg3,那么输出将是 53113。这是预期的。该程序及其输出可以在线验证onlinegdb.com/online_c++_compiler
        • 在上面的问题中,用户将预期输出指定为53 1 0 13 而不是53113。其他答案满足此要求。
        • 要求是从字符串中提取所有数字。指定的程序符合预期。可以通过在打印时在数字之间添加空格或为每个单词引入额外的 for 循环来更改格式。
        • 我现在用不使用正则表达式的第二种方法更新了答案。
        【解决方案6】:

        如何提取所有数字?

        当您知道输入数字都是十六进制值时......(以及多少)

        stringstream ss ("5a3 1f a0aaaa f1fg3"); 
        for (int i=0; i<4; ++i)
        {
           int k;
           ss >> hex >> k;
           cout << k << endl;
        }
        

        有输出

        1443
        31
        10529450
        3871
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-06-24
          • 1970-01-01
          • 1970-01-01
          • 2011-01-29
          • 1970-01-01
          • 1970-01-01
          • 2020-09-28
          • 1970-01-01
          相关资源
          最近更新 更多