【问题标题】:Loading a file into a vector<char>将文件加载到向量<char>
【发布时间】:2011-11-06 16:45:50
【问题描述】:

我想将文本文件的内容加载到vector&lt;char&gt;(或任何字符输入迭代器,如果可能的话)。目前我的代码如下所示:

std::vector<char> vec;
std::ifstream file("test.txt");
assert(file.is_open());
while (!(file.eof() || file.fail())) {
    char buffer[100];
    file.read(buffer, 100);
    vec.insert(vec.end(), buffer, buffer + file.gcount());
}

我不喜欢手动使用缓冲区(为什么是 100 个字符?为什么不是 200 或 25 或其他什么?),或者这需要大量的行。代码看起来非常丑陋且非 C++。有没有更直接的方法?

【问题讨论】:

  • 看看这个回复:stackoverflow.com/questions/132358/…。它在中间状态下完全符合您的要求,甚至从字符串(或vector&lt;char&gt;)构建流。
  • @Diego - 把它写下来作为答案,我会接受的。这正是我想要的。
  • 完成了,虽然它可能被视为重复问题?
  • 回复assert(file.is_open());:不要那样做! assert 是一个可以扩展为空的宏(例如,发布模式),即使 assert 确实生成代码,它也无济于事。丢失的文件是用户错误,而不是程序员错误。一般准则:使用assert 检测程序员错误。使用其他东西,anything else,用于用户错误。
  • @David Hammen - 我知道......目前这不完全是生产代码。不过我会修复问题中的代码。

标签: c++ iostream


【解决方案1】:

如果你想避免逐字符读取:

if (!file.eof() && !file.fail())
{
    file.seekg(0, std::ios_base::end);
    std::streampos fileSize = file.tellg();
    vec.resize(fileSize);

    file.seekg(0, std::ios_base::beg);
    file.read(&vec[0], fileSize);
}

【讨论】:

  • 很好的解决方案,但这安全吗?
  • @doron:“安全”在什么意义上?
  • 如果“安全”这个词是关于不使应用程序崩溃并将值正确复制到向量,我认为是。但是在函数file.read(&amp;vec[0], fileSIZe) 执行之后。向量大小vec.size() 仍然为零,vec.empty() 为真。不确定这在您的应用中是否安全。
  • 其实在google之后,我找到了this article。答案在 Bad idea #2 部分。基本上,它会导致未定义的行为。
  • 对于标准的坚持者,重要的是要记住实际上没有人编写完全符合标准的代码。就像我怀疑如果 char 是 9 位代码会起作用。所以有时依赖事实标准是可以的。
【解决方案2】:

我觉得是这样的,但是没有环境可以测试:

std::copy(std::istream_iterator<char>(file), std::istream_iterator<char>(), std::back_inserter(vec));

您可能必须使用 io 操纵器来处理换行符/空格等问题。

编辑:如 cmets 中所述,可能会影响性能。

【讨论】:

  • @Diego:可能,我不知道std的实现细节,无法测试。此外,这不一定是一个问题,但确实很好。
  • 进一步注意,如果性能要求对此不太严格,并且如果提问者真的只需要“任何字符输入迭代器”,那么就不需要容器。 std::istream_iterator&lt;char&gt;(file), std::istream_iterator&lt;char&gt;() 已经是请求的 InputIterator 对。
  • 您的流将被缓冲,因此内核调用的开销应该很低。 istream 迭代器也可以在底层使用 memcpy。有兴趣看看这个和 Eugene 的解决方案之间的性能差异,但我认为差异不会很大。
  • 等一下,我在撒谎,你说得对,需要一个 io 操纵器来处理空格。所需的迭代器对是std::istream_iterator&lt;char&gt;(file&gt;&gt;std::noskipws), std::istream_iterator&lt;char&gt;()
【解决方案3】:

另一种方法,首先使用rdbuf() 将整个文件读取到std::stringstream

#include <fstream>
#include <sstream>
#include <vector>
#include <string>

// for check:
#include <algorithm>
#include <iterator>
#include <iostream>

int main() {
   std::ifstream file("test.cc");
   std::ostringstream ss;
   ss << file.rdbuf();
   const std::string& s = ss.str();
   std::vector<char> vec(s.begin(), s.end());

   // check:
   std::copy(vec.begin(), vec.end(), std::ostream_iterator<char>(std::cout));
}

【讨论】:

    【解决方案4】:

    使用迭代器:

    #include <iterator>
    
    istream_iterator<char> data( file );
    istream_iterator<char> end;
    vec.insert( std::back_inserter(vec), data, end );
    

    【讨论】:

    • 这不是一次读取一个字符并且跳过空格吗?
    【解决方案5】:

    有很多很好的反应。谢谢大家!我决定使用的代码是这样的:

    std::vector<char> vec;
    std::ifstream file;
    file.exceptions(
        std::ifstream::badbit
      | std::ifstream::failbit
      | std::ifstream::eofbit);
    //Need to use binary mode; otherwise CRLF line endings count as 2 for
    //`length` calculation but only 1 for `file.read` (on some platforms),
    //and we get undefined  behaviour when trying to read `length` characters.
    file.open("test.txt", std::ifstream::in | std::ifstream::binary);
    file.seekg(0, std::ios::end);
    std::streampos length(file.tellg());
    if (length) {
        file.seekg(0, std::ios::beg);
        vec.resize(static_cast<std::size_t>(length));
        file.read(&vec.front(), static_cast<std::size_t>(length));
    }
    

    显然,这不适合超大文件或性能关键代码,但对于一般用途来说已经足够了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-11-19
      • 2012-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-07
      相关资源
      最近更新 更多