【问题标题】:How to write to a memory buffer with a FILE*?如何使用 FILE* 写入内存缓冲区?
【发布时间】:2010-10-07 01:47:27
【问题描述】:

有没有办法将内存缓冲区创建为 FILE*。在 TiXml 中,它可以将 xml 打印到 FILE* 但我似乎无法将其打印到内存缓冲区。

【问题讨论】:

标签: c++ c


【解决方案1】:

有一种 POSIX 方法可以将内存用作 FILE 描述符:fmemopenopen_memstream,具体取决于您想要的语义:Difference between fmemopen and open_memstream

【讨论】:

  • 在任何 C/C++ 标准中都没有定义 fmemopen 和 open_memstream!
  • POSIX 不是 C/C++ 标准!这是一个严重依赖于 C 标准的标准,但仍然只适用于由 C/C++ 支持/开发的操作系统的子集!更不用说 POSIX 与 C/C++ 标准的某些方面冲突了……
  • 如果主要的桌面操作系统不遵循 POSIX,我们如何“一般来说,在实现 POSIX 的系统上编程”?我的观点是,任何 C/C++ 标准都没有定义 fmemopen 或 open_memstream! - 您声称并支持 POSIX 200809 - 因此声称它们“符合标准”是不真实的。有趣的是,到目前为止,你唯一的反应是在每条评论中提到我“大量”使用感叹号,而不是承认这些函数符合标准的说法是错误的......
  • “标准的好处是有很多可供选择。”我想生活在现实世界中,副C使徒的纯粹涅槃,将不得不抚慰我的良心。
  • 那句话是对的,但是这些函数都没有在 C/C++ 标准中定义......(这两个是唯一与原始问题相关的函数)
【解决方案2】:

我想正确的答案是凯文。但这里有一个用 FILE *.请注意,如果缓冲区大小(此处为 100000)太小,则会丢失数据,因为在刷新缓冲区时会写出数据。此外,如果程序调用 fflush() 会丢失数据。

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    FILE *f = fopen("/dev/null", "w");
    int i;
    int written = 0;
    char *buf = malloc(100000);
    setbuffer(f, buf, 100000);
    for (i = 0; i < 1000; i++)
    {
        written += fprintf(f, "Number %d\n", i);
    }
    for (i = 0; i < written; i++) {
        printf("%c", buf[i]);
    }
}

【讨论】:

  • 它只有在可以防止 stdio 刷新缓冲区的情况下才有效,我猜其中的细节没有标准化。
  • setbuf()setvbuf()(不是 setbuffer())是 ISO-C,所以一旦你使用它们,它应该是可移植的。如果缓冲模式是完全缓冲的,则应该尝试将缓冲区完全填满;在 Windows 上,您必须使用“NUL”而不是“/dev/null”;您还应该以二进制模式“wb”打开文件
【解决方案3】:

fmemopen 可以从缓冲区创建文件,这对你有意义吗?

【讨论】:

    【解决方案4】:

    我写了一个如何创建内存文件的简单示例:

    #include <unistd.h> 
    #include <stdio.h> 
    
    int main(){
      int p[2]; pipe(p); FILE *f = fdopen( p[1], "w" );
    
      if( !fork() ){
        fprintf( f, "working" );
        return 0;
      }
    
      fclose(f); close(p[1]);
      char buff[100]; int len;
      while( (len=read(p[0], buff, 100))>0 )
        printf(" from child: '%*s'", len, buff );
      puts("");
    }
    

    【讨论】:

      【解决方案5】:

      C++ basic_streambuf继承

      在 C++ 中,如果可以,您应该避免使用FILE*

      仅使用 C++ 标准库,可以创建一个透明地使用文件或内存 IO 的单一接口。

      这使用了Setting the internal buffer used by a standard stream (pubsetbuf)中提到的技术

      #include <cassert>
      #include <cstring>
      #include <fstream>
      #include <iostream>
      #include <ostream>
      #include <sstream>
      
      /* This can write either to files or memory. */
      void write(std::ostream& os) {
          os << "abc";
      }    
      
      template <typename char_type>
      struct ostreambuf : public std::basic_streambuf<char_type, std::char_traits<char_type> > {
          ostreambuf(char_type* buffer, std::streamsize bufferLength) {
              this->setp(buffer, buffer + bufferLength);
          }
      };
      
      int main() {
          /* To memory, in our own externally supplied buffer. */
          {
              char c[3];
              ostreambuf<char> buf(c, sizeof(c));
              std::ostream s(&buf);
              write(s);
              assert(memcmp(c, "abc", sizeof(c)) == 0);
          }
      
          /* To memory, but in a hidden buffer. */
          {
              std::stringstream s;
              write(s);
              assert(s.str() == "abc");
          }
      
          /* To file. */
          {
              std::ofstream s("a.tmp");
              write(s);
              s.close();
          }
      
          /* I think this is implementation defined.
           * pusetbuf calls basic_filebuf::setbuf(). */
          {
              char c[3];
              std::ofstream s;
              s.rdbuf()->pubsetbuf(c, sizeof c);
              write(s);
              s.close();
              //assert(memcmp(c, "abc", sizeof(c)) == 0);
          }
      }
      

      不幸的是,FILE*fstream 似乎无法互换:Getting a FILE* from a std::fstream

      【讨论】:

      • 不要忘记在ostreambuf 构造函数中,您也可以设置setg 以从流中读取。例如this-&gt;setg(buffer, buffer, buffer + bufferLength);
      【解决方案6】:

      您可以使用文档所述的TiXMLPrinterCStr 方法:

      TiXmlPrinter 非常有用 需要:

      1. 打印到内存(尤其是在非 STL 模式下)
      2. 控制格式(行尾等)

      【讨论】:

        【解决方案7】:

        https://github.com/Snaipe/fmem 是针对不同平台/版本特定的内存流实现的包装器

        它依次尝试以下实现:

        • open_memstream。
        • fopencookie,具有不断增长的动态缓冲区。
        • funopen,具有不断增长的动态缓冲区。
        • WinAPI 临时内存支持文件。

        当没有其他方法可用时,fmem 回退到 tmpfile()

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-05-21
          • 1970-01-01
          相关资源
          最近更新 更多