设置标准流使用的内部缓冲区 (pubsetbuf)

Posted

技术标签:

【中文标题】设置标准流使用的内部缓冲区 (pubsetbuf)【英文标题】:Setting the internal buffer used by a standard stream (pubsetbuf) 【发布时间】:2010-12-02 10:12:16 【问题描述】:

我正在编写一个需要将数据写入现有缓冲区的子程序,我想使用stringstream 类来方便数据的格式化。

最初,我使用以下代码将流的内容复制到缓冲区中,但想避免这种解决方案,因为它复制了太多数据。

#include <sstream>
#include <algorithm>

void FillBuffer(char* buffer, unsigned int size)

    std::stringstream message;
    message << "Hello" << std::endl;
    message << "World!" << std::endl;

    std::string messageText(message.str());
    std::copy(messageText.begin(), messageText.end(), buffer);

这是我发现streambuf::pubsetbuf()方法的时候,简单地将上面的代码改写如下。

#include <sstream>

void FillBuffer(char* buffer, unsigned int size)

    std::stringstream message;
    message.rdbuf()->pubsetbuf(buffer, size);

    message << "Hello" << std::endl;
    message << "World!" << std::endl;

不幸的是,这在 Visual Studio 2008 附带的 C++ 标准库实现下不起作用; buffer 保持不变。

我查看了pubsetbuf 的实现,结果发现它实际上“什么都不做”。

virtual _Myt *__CLR_OR_THIS_CALL setbuf(_Elem *, streamsize)
   // offer buffer to external agent (do nothing)
    return (this);

这似乎是给定 C++ 标准库实现的限制。配置流以将其内容写入给定缓冲区的推荐方法是什么?

【问题讨论】:

@Steve 我也在用VS2008,但是已经实现了! @Arak -- 我的pubsetbuf() 实现转发到私有setbuf() 操作,如上实现。您能否确认您的实施有所不同? 这在 VS2010 中仍然适用 在 VS2017 中仍然如此 【参考方案1】:

在对这个问题进行更多研究并仔细检查我的代码后,我发现a post 建议使用手动编码的std::streambuf 类。这段代码背后的想法是创建一个streambuf,它初始化其内部以引用给定的缓冲区。代码如下。

#include <streambuf>

template <typename char_type>
struct ostreambuf : public std::basic_streambuf<char_type, std::char_traits<char_type> >

    ostreambuf(char_type* buffer, std::streamsize bufferLength)
    
        // set the "put" pointer the start of the buffer and record it's length.
        setp(buffer, buffer + bufferLength);
    
;

现在,如果您查看my original code,您会注意到我并不真正需要stringstream。我真正需要的只是一种使用iostream 库写入外部缓冲区的方法,而std::ostream 是解决此问题的更好类型。顺便说一句,我怀疑这就是 Boost.IOStreams 中的 array_sink 类型的实现方式。

这是使用我的ostreambuf 类型的修改代码。

#include <ostream>
#include "ostreambuf.h"  // file including ostreambuf struct from above.

void FillBuffer(char* buffer, unsigned int size)

    ostreambuf<char> ostreamBuffer(buffer, size);
    std::ostream messageStream(&ostreamBuffer);

    messageStream << "Hello" << std::endl;
    messageStream << "World!" << std::endl;

【讨论】:

这段代码是否没有 null 终止缓冲区的问题?考虑到这是一个 hello world 示例,我猜缓冲区应该是空终止的。【参考方案2】:

看起来像是(正式弃用,但仍然是标准的)std::strstream 的工作。您还可以查看Boost.IOStreams 库,尤其是array_sink。

【讨论】:

std::strstream 不就是 std::stringstream 的同义词吗?为什么会有不同的实施方式? 不,因为它允许用户直接提供和管理缓冲区,所以效率更高(使用起来也很棘手)。例如,请参阅gotw.ca/publications/mill19.htm 中的选项 #4。 @Clifford: kera.name/articles/2008/09/…【参考方案3】:

正如您发布的链接所说:“具体实现可能会有所不同”。

你不能简单地返回 std::string 对象,然后在需要 char 缓冲区的地方使用 std::string::c_str() 或 std::string::data() 吗?

或者使用 C 库中的 sprintf(),那么整个操作可以在传递的缓冲区中完成。由于这种方式可能会导致潜在的缓冲区溢出,并且您使用的是 Visual C++,因此您可以考虑sprintf_s

【讨论】:

不幸的是,我不能使用c_str(),因为缓冲区属于另一种类型。我希望用一些东西填充该缓冲区,以便其他类型可以对其进行操作。 sprintf_s() 会很好用,我使用它应该没有问题。

以上是关于设置标准流使用的内部缓冲区 (pubsetbuf)的主要内容,如果未能解决你的问题,请参考以下文章

标准是不是要求流构造函数不访问流缓冲区?

标准文件IO详解---标准输入流标准输出流和标准错误流

C中的管道,用于读取标准输入的缓冲区

什么是内存流和文件流?

IO流01_字节字符流缓冲流标准输入输出流打印流

C标准输出流