【问题标题】:Reading formatted data with C++'s stream operator >> when data has spaces当数据有空格时,使用 C++ 的流运算符 >> 读取格式化数据
【发布时间】:2011-01-21 06:47:06
【问题描述】:

我有以下格式的数据:

4:你好吗? 10:生日快乐 1:紫猴洗碗机 200:小号天鹅的祖先领土命令

数字可以是 1 到 999 之间的任意值,字符串长度最多为 255 个字符。我是 C++ 新手,似乎有一些消息来源建议使用流的 >> 运算符提取格式化数据,但是当我想提取字符串时,它会在第一个空格字符处停止。有没有办法配置流以仅在换行符或文件末尾停止解析字符串?我看到有一个getline 方法可以提取一整行,但是我仍然需要手动拆分它[使用find_first_of],不是吗?

有没有一种简单的方法可以仅使用 STL 来解析这种格式的数据?

【问题讨论】:

  • C++ 中的流是我讨厌 C++ 的原因之一。
  • 由于我是 C++ 新手,我希望流是最终导致“哦哦,这很聪明”顿悟的事情之一,但在您发表评论后,我开始认为“永远不会发生。 :(

标签: c++ stl stream formatted-input


【解决方案1】:

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

#include <string>
#include <deque>
#include "strtk.hpp"

int main()
{
   struct line_type
   {
      unsigned int id;
      std::string str;
   };

   std::deque<line_type> line_list;

   const std::string file_name = "data.txt";

   strtk::for_each_line(file_name,
                        [&line_list](const std::string& line)
                        {
                           line_type temp_line;
                           const bool result = strtk::parse(line,
                                                            ":",
                                                            temp_line.id,
                                                            temp_line.str);
                           if (!result) return;
                           line_list.push_back(temp_line);
                        });

   return 0;
}

更多例子可以找到Here

【讨论】:

    【解决方案2】:

    您可以在使用std::getline 之前读取数字,它从流中读取并存储到std::string 对象中。像这样的:

    int num;
    string str;
    
    while(cin>>num){
        getline(cin,str);
    
    }
    

    【讨论】:

    • 看起来很干净;我认为将cin 替换为给我的istream 是安全的?
    • 如果你从一个文件中读取,你可以用 valid ifstream 对象替换 cin。
    • 我刚刚获得了一个流,我的代码应该解析数据,对其进行操作并将其写入另一个流。我不创建任何一个流。我假设如果istreamostream 无效,我的过滤器不会被调用,但同时我不认为这是我的任何担心。垃圾进垃圾出 :) 。 . .或者可能是段错误中的垃圾。
    • 我有一个额外的char 变量并使用while (cin &gt;&gt; num &gt;&gt; dummy) 来去掉冒号字符。
    【解决方案3】:

    你已经被告知std::getline,但他们没有提到你可能会发现有用的一个细节:当你调用getline时,你还可以传递一个参数告诉它要处理什么字符输入结束。要读取您的号码,您可以使用:

    std::string number;
    std::string name;
    
    std::getline(infile, number, ':');
    std::getline(infile, name);   
    

    这会将直到“:”的数据放入number,丢弃“:”,并将该行的其余部分读入name

    如果您想使用&gt;&gt; 读取数据,您也可以这样做,但它有点困难,并且深入研究了大多数人从未接触过的标准库区域。流具有关联的locale,用于格式化数字和(重要的是)确定什么构成“空白”。您可以定义自己的语言环境,将“:”定义为空格,将空格 (" ") 定义为 not 空格。告诉流使用该语言环境,它会让您直接读取数据。

    #include <locale>
    #include <vector>
    
    struct colonsep: std::ctype<char> {
        colonsep(): std::ctype<char>(get_table()) {}
    
        static std::ctype_base::mask const* get_table() {
            static std::vector<std::ctype_base::mask> 
                rc(std::ctype<char>::table_size,std::ctype_base::mask());
    
            rc[':'] = std::ctype_base::space;
            rc['\n'] = std::ctype_base::space;
            return &rc[0];
        }
    };
    

    现在要使用它,我们用语言环境“灌输”流:

    #include <fstream>
    #include <iterator>
    #include <algorithm>
    #include <iostream>
    
    typedef std::pair<int, std::string> data;
    
    namespace std { 
        std::istream &operator>>(std::istream &is, data &d) { 
           return is >> d.first >> d.second;
        }
        std::ostream &operator<<(std::ostream &os, data const &d) { 
            return os << d.first << ":" << d.second;
        }
    }
    
    int main() {
        std::ifstream infile("testfile.txt");
        infile.imbue(std::locale(std::locale(), new colonsep));
    
        std::vector<data> d;
    
        std::copy(std::istream_iterator<data>(infile), 
                  std::istream_iterator<data>(),
                  std::back_inserter(d));
    
        // just for fun, sort the data to show we can manipulate it:
        std::sort(d.begin(), d.end());
    
        std::copy(d.begin(), d.end(), std::ostream_iterator<data>(std::cout, "\n"));
        return 0;
    }
    

    现在你知道为什么图书馆的那部分如此被忽视了。从理论上讲,让标准库为您完成工作是很棒的——但实际上,大多数情况下,您自己做这种工作会更容易。

    【讨论】:

      【解决方案4】:

      只需使用 getline 逐行(整行)读取数据并解析即可。
      解析使用 find_first_of()

      【讨论】:

        【解决方案5】:
        int i; char *string = (char*)malloc(256*sizeof(char)); //since max is 255 chars, and +1 for '\0' scanf("%d:%[^\n]s",&i, string); //use %255[^\n]s for accepting 255 chars max irrespective of input size printf("%s\n", string);

        它是 C 语言,也可以在 C++ 中工作。 scanf 提供更多控制,但没有错误管理。所以谨慎使用:)。

        【讨论】:

        • 看来m 标志没有标准化,所以我不能使用它。但是,再一次,这不会仍然只读取第一个空白字符而不是行尾吗?
        • 它仍然只读取该行的第一个单词,而不是整行,并且您的代码中有错误:您提供的是i,但scanf 需要一个指针i (&amp;i)。
        猜你喜欢
        • 2011-11-18
        • 1970-01-01
        • 1970-01-01
        • 2017-06-30
        • 2017-10-11
        • 2012-08-13
        • 1970-01-01
        • 2013-06-19
        • 2011-04-03
        相关资源
        最近更新 更多