用C ++流式传输二进制文件

时间:2015-05-21 23:50:45

标签: c++ iostream

来自C,我正在尝试使用C ++,并且偶然发现了一些简单的事情,比如使用ifstream将文件中的二进制数据读入缓冲区。在我看来,我有三个选项来从文件中读取数据:

  • get(),它获取一个单个字符,这对于将更大量的数据读入内存缓冲区而言似乎很奇怪且效率低下;
  • read(),它不返回实际读取的字符数;和
  • readsome(),如果我理解正确的话,只返回先前缓冲的数据,但不会从实际文件中读取任何新内容。

令我感到特别奇怪的是read()函数,在我看来完全无法看到它如何不告诉它实际放入提供的缓冲区的字节数。然而,我看到使用它的所有示例代码似乎都验证了这种情况,并且通常寻求文件的末尾来获取文件的大小并在此后分配缓冲区。显然,这对流数据不起作用。

那么实际上如何在C ++中使用非文本数据流式传输文件/管道/套接字?是否有比ifstream更好的设施?

2 个答案:

答案 0 :(得分:5)

  

令我印象深刻的是read()函数,在我看来完全无法看到它如何不能告诉它实际放入提供的缓冲区的字节数。

read()在1)已读取请求的字符数,2)达到EOF或3)发生错误之前不会退出。

read()退出后,如果读取成功,您可以调用gcount()来查找缓冲区中读取了多少个字符。如果在读取期间达到EOF,则流的eofbit状态将设置为true,gcount()将返回的字符数少于您请求的字符数。

如果读取失败,则流的failbit和/或badbit状态将设置为true。

std::ifstream ifs(...);
if (is) {
    // stream opened...
    //...
    ifs.read(buffer, sizeof(buffer));
    if (ifs) {
        // read succeeded, EOF may have been reached...
        std::streamsize numInBuf = ifs.gcount();
        //...
    } else {
        // read failed...
    }
    //...
} else {
    // stream not opened...
}

如果您使用流的exceptions()方法通过异常启用错误报告,则如果失败与您启用了异常的错误位相匹配,则可能会引发std::ios_base::failure异常。

std::ifstream ifs;
ifs.exceptions(std::ifstream::badbit | std::ifstream::failbit);
try {
    ifs.open(...);
    // stream opened...
    //...
    ifs.read(buffer, sizeof(buffer));
    // read succeeded, EOF may have been reached...
    std::streamsize numInBuf = ifs.gcount();
    //...
} catch (const std::ios_base::failure &e) {
    // stream failure...
}
  

那么实际上如何在C ++中使用非文本数据流式传输文件/管道/套接字?是否有比ifstream更好的设施呢?

std::ifstream专为基于文件的流而设计。对于管道,如果您的平台可以通过标准文件API访问管道,std::ifstream应该可以正常工作。但是对于套接字,您需要使用更合适的std::basic_istream派生类,或者至少使用附加了自定义std::istream派生类的标准std::streambufexample

答案 1 :(得分:0)

  

“让我感到特别奇怪的是read()函数,在我看来完全无法看到它如何不能告诉它实际放入提供的缓冲区的字节数。”

那么,您可以指定一次可以读取多少字节作为最大值,并且可以使用std::ifstream::eof()检查该流是否已经用尽以检查状态。

如果您想知道文件中已有多少字节可用,并相应地分配您的读取缓冲区,您可以事先检查整个文件大小,只需对the reference中的示例代码稍作修改:

// read entire file into string
std::ifstream is("test.txt", std::ifstream::binary);
if (is) {
    // get length of file:
    is.seekg(0, is.end);
    int length = is.tellg();
    is.seekg(0, is.beg);

    std::string str;
    str.resize(length, ' '); // ******* reserve space ********
    char* begin = &*str.begin();

    is.read(begin, length);
    is.close();

    std::cout << str << "\n";
} else {
    std::cout << "Could not open test.txt\n";
}

请注意,您的问题的关键点在于,如果您能够一次性完全分配length,或者如果您不能分开阅读较小的块并按顺序解析这些块提供一个能够保存完整文件大小的缓冲区。

  

“显然,这不适用于流媒体数据。”

这只是将接收到的数据块从异步任务解耦到更高级别线程的问题,该线程以您希望的方式以流式方式使用数据。