将 stdout 置于二进制模式的可移植方式

Portable way to put stdout in binary mode

提问人:John Forkosh 提问时间:11/15/2016 最后编辑:John Forkosh 更新时间:11/16/2016 访问量:1614

问:

我正在编写用作 cgi 的 C 程序,用于生成和输出 gif 图像。它们用于带有 form 的标签的 HTML 页面中,其中查询字符串参数描述了要由 CGI 生成的图像。<img src="/cgi-bin/gifprogram.cgi?param=val&etc">

像这样调用的 C 程序 cgi 会将其输出发送到 stdout。在本例中,输出是包含大量二进制(不可打印)字节的 gif 图像。这在 Unix/Linux 上已经运行良好。但 Windows 显然需要一个单独的非可移植调用(以及一些特定于 Windows 的调用)。否则,您会遇到通常的 cr/lf 问题,即每个0x0A前面都有一个虚假0x0D。这在 gif :(中看起来并不好_setmode(_fileno(stdout),_O_BINARY)#include

是否有任何可移植的方法来解决这个问题,使用ANSI标准的C,没有任何特定于平台的语法,并且没有很多东西来尝试检测Windows编译环境?此外,一些 Windows 编译器显然使用,而另一些编译器则使用 ,我还必须尝试检测。#ifdef_setmode(_fileno(stdout),_O_BINARY)setmode(fileno(stdout),O_BINARY)

>>编辑<<回复评论...

谢谢,伙计们。我显然不得不采用一些完全符合 posix 的/可移植的程序,并将它们用于 Windows [注意,JoNaThAn:真的,不需要编辑来大写:),——我个人的写作风格是故意在语法上松散的]。

以下是我正在考虑引入的一些愚蠢的第一部分。据我所知,使用 mingw 似乎有效。它是否足够,即适用于所有或大多数其他编译器?是否有必要,即任何简短整洁/快速和肮脏的方法来用更少/更易读的代码行来完成同样的事情?

/* ---
 * windows-specific header info
 * ---------------------------- */
#ifndef WINDOWS                 /* -DWINDOWS not supplied by user */
  #if defined(_WINDOWS) || defined(_WIN32) || defined(WIN32) \
  ||  defined(DJGPP)            /* try to recognize windows compilers */ \
  ||  defined(_USRDLL)          /* must be WINDOWS if compiling for DLL */
    #define WINDOWS             /* signal windows */
  #endif
#endif
#ifdef WINDOWS                  /* Windows opens stdout in char mode, and */
  #include <fcntl.h>            /* precedes every 0x0A with spurious 0x0D.*/
  #include <io.h>               /* So emitcache() issues a Win _setmode() */
                                /* call to put stdout in binary mode. */
  #if defined(_O_BINARY) && !defined(O_BINARY)  /* only have _O_BINARY */
    #define O_BINARY _O_BINARY  /* make O_BINARY available, etc... */
    #define setmode  _setmode
    #define fileno   _fileno
  #endif
  #if defined(_O_BINARY) || defined(O_BINARY)  /* setmode() now available */
    #define HAVE_SETMODE        /* so we'll use setmode() */
  #endif
  #if defined(_MSC_VER) && defined(_DEBUG) /* MS VC++ in debug mode */
    /* to show source file and line numbers where memory leaks occur... */
    #define _CRTDBG_MAP_ALLOC   /* ...include this debug macro */
    #include <crtdbg.h>         /* and this debug library */
  #endif
  #define ISWINDOWS 1
#else
  #define ISWINDOWS 0
#endif

而后续的相应代码,确保 stdout 处于二进制模式是

    #if ISWINDOWS                           /* compiling for win... */
      #ifdef HAVE_SETMODE                   /* try to use setmode()*/
        if ( setmode ( fileno (stdout), O_BINARY) /* to set stdout */
        == -1 ) /* handle error here*/ ;    /* to binary mode */
      #else                                 /* setmode not available */
        #if 1                               /* so try this...*/
          freopen ("CON", "wb", stdout);    /* freopen stdout binary */
        #else                               /* or maybe this... */
          stdout = fdopen (STDOUT_FILENO, "wb"); /*fdopen stdout binary*/
        #endif                              /* done */
      #endif                                /* " */
    #endif                                  /* " */

您会注意到我正在尝试建议 setmode() 替代方案。虽然我的替代测试没有成功,但我保留了语法以备将来参考,以防万一其中一些最终被证明是有用的。

C Linux Windows IO 可移植性

评论

4赞 Michael Foukarakis 11/15/2016
没有便携式方法。您必须单独处理每个平台。
1赞 Michael Foukarakis 11/15/2016
我不知道有这样的编译器开关。
1赞 Jon Trauntvein 11/15/2016
即使有一个编译器开关,我也怀疑它是否有任何好处,因为进程的输入和输出句柄是由调用它的进程设置的。我怀疑输入和输出流甚至在程序运行之前就已设置为文本模式。
2赞 Jonathan Leffler 11/15/2016
之所以出现 vs 问题,是因为 Microsoft 认为 POSIX 函数会污染用户的命名空间,因此它使用前导下划线重命名它们。具有讽刺意味的是,它首先还重命名了 POSIX 函数。setmode()_setmode()_setmode()setmode()
2赞 Jonathan Leffler 11/16/2016
请注意,如果您的程序用作 CGI 程序,则其标准输出几乎可以肯定不是控制台。因此,打开可能不正确;它将中断由启动 CGI 程序的 Web 服务器创建的任何重定向。显然,这段代码应该被隔离成一个函数(也许)在一个文件里,要么在Unix上什么都不做,要么在Windows上做一些工作。程序无条件调用该函数。包含该函数的文件的编译令人毛骨悚然;其余的都很干净。CON:void setbinmode(FILE *fp)

答: 暂无答案