【问题标题】:c++ ifstream function and field separatorsc++ ifstream 函数和字段分隔符
【发布时间】:2010-11-16 18:35:42
【问题描述】:

对于这个程序,我只使用了 shell 脚本中数据文件的字段分隔符。但我正在尝试使用标准库函数 ifstream() 从数据文件中读取。唯一的问题是我得到的数据是这样的

A:KT5:14:行政办公桌:

这是一个哈希表,我需要将数据结构和事务类型的行中的值分开。我一直在网上四处寻找,并没有在字段分隔符上找到太多内容,而且我发现的内容非常混乱。

那么问题来了,有没有办法用 ifstream 函数设置字段分隔符,还是我应该使用另一个标准库 i/o 函数?

谢谢。

【问题讨论】:

    标签: c++ file-io ifstream


    【解决方案1】:

    @Steve Townsend 已经指出了一种可能性。如果您更喜欢使用operator>> 而不是std::getline,您也可以这样做。 istream 始终将空格视为分隔符。每个流都有一个关联的语言环境,每个语言环境都包含一个ctype 方面。 ctype facet 是 istream 用来确定哪些输入字符是空格的。

    在您的情况下,您显然希望流仅将换行符和冒号视为“空白”(即分隔符),而实际的空格字符仅被视为“普通”字符,而不是分隔符。

    为此,您可以像这样创建一个 ctype facet:

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

    要使用这个,你必须使用这个方面给流“灌输”一个语言环境:

    int main() {
        std::stringstream input("A:KT5:14:executive desk:");
    
        // have the stream use our ctype facet:
        input.imbue(std::locale(std::locale(), new field_reader()));
    
        // copy fields from the stream to standard output, one per line:
        std::copy(std::istream_iterator<std::string>(input), 
                  std::istream_iterator<std::string>(),
                  std::ostream_iterator<std::string>(std::cout, "\n"));
        return 0;
    }
    

    然而,我是第一个承认这有一些缺点的人。首先,语言环境和方面的文档通常很差,因此大多数 C++ 程序员可能会发现这相当难以理解(尤其是当所有实际工作都发生在“幕后”时,可以这么说)。

    另一种可能性是使用Boost Tokenizer。老实说,这是一个更多的工作——它需要你做一些事情,比如读取一个字符串,然后单独分解它。同时,它有据可查,广为人知,并且更符合人们对如何做这样的事情的先入之见,尽管有额外的复杂性,但仍有相当多的人可能会发现它更容易遵循。

    【讨论】:

    • 我同意很多 C++ 开发人员对语言环境/方面的理解很差。但那是因为很少有人真正为 I18N 或 L10N 的代码而烦恼,因此从不费心学习这些东西。但这是尽可能频繁地提出它的另一个原因,以表明如果正确使用它会使主要代码更容易编写和理解。这是我从方面学到的另一个技巧,并打算尽快使用它。
    【解决方案2】:

    getline 让您可以选择指定分隔符。然后,您可以将流中的输入读取为string 的序列,由_Delim 分隔:

    template<class CharType, class Traits, class Allocator>
       basic_istream< CharType, Traits >& getline(
           basic_istream< CharType, Traits >& _Istr,
           basic_string< CharType, Traits, Allocator >& _Str,
           CharType _Delim
       );
    

    如果这是统一结构的数据,那么定义一个结构来包含它并实现operator&gt;&gt; 以使用运算符代码内部的上述函数从流中加载每个实例可能会很有用。

    如果您必须处理多行(因此换行符是记录分隔符和 : 字段分隔符),请使用 basic_istream::getline 将每一行依次加载到 stringstream 中,然后将该行后处理为字段,如图所示。

    【讨论】:

    • @onemasse,erm:声明实际上看起来像:istream&amp; getline ( istream&amp; is, string&amp; str, char delim );,现在还不错吧? :),更不用说std::getline 为您提供了超过等效 C 的东西! :)
    • @onemasse - 编码后看起来并没有那么糟糕。第一个参数是ifstream&amp;,第二个是string&amp;,最后一个参数是char
    • @Nim 好的,还不错。毕竟我可能会尝试 C++。 :)
    • @Nim 你觉得 std::getline 比 C 语言中的等价物给你什么?我很好奇,我不想发起一场语言抨击战争。
    • @onemasse - 作为一个问题提出这个问题并获得一些分数(如果还没有这样做的话)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多