【问题标题】:Buffer handling for stdout标准输出的缓冲区处理
【发布时间】:2016-09-06 17:38:01
【问题描述】:

我尝试对终端输出功能进行单元测试。该函数将数据流式传输到标准输出,所以我的想法是控制标准输出缓冲区并检查该函数是否正确地将正确的数据写入缓冲区。

在我看来,setvbuf 是实现这一目标的理想选择。

我使用 setvbuf 重定向标准输出以使用我自己指定的缓冲区。

我使用了流模式 _IOFBF,因此理论上只有在缓冲区已满时才会发生内部刷新。我系统上的 BUFSIZ 大小为 8192 字节......对于被测函数的输出来说已经足够大了,在调用期间没有内部刷新。

我的来源目前看起来像:

char buffer [BUFSIZ] = {0};

/* any outputs before redirecting */
printf("0\n");

/* output any remaining data */
fflush(stdout); 
/* redirect to my buffer with full buffering */
setvbuf(stdout,buffer,_IOFBF,BUFSIZ); 

/* testcode ... output of the function under test */
printf("1\n");
printf("2\n");

/* output any remaining data */
fflush(stdout);
/* restore internal buffering with line buffering */
setvbuf(stdout,NULL,_IOLBF,BUFSIZ);

/* let us see what is in our buffer */
printf("%s",buffer);

调试此代码显示“2\n”在测试代码部分覆盖缓冲区中的“1\n”(gcc 5.4.0 x64 GNU libc 版本:2.23)

输出是:

0
1
2
2

但我期待:

0
1
2
1
2

如有任何提示,我将不胜感激。

【问题讨论】:

  • 我无法重现错误;我得到了预期的输出。
  • 感谢您的验证。现在不确定这是否是我系统上 clib 缓冲区处理中的错误。你用什么编译器+版本来验证?
  • 也许使用setvbuf() 太晚了? C11dr 7.21.5.6 2 “setvbuf 函数只能在 stream 指向的流与打开的文件相关联之后使用 并且在执行任何其他操作之前(对 setvbuf 的不成功调用除外)在直播中。”
  • 我使用的 gcc 版本 4.2.1,但可能存在未定义的行为。

标签: c redirect printf buffer stdout


【解决方案1】:

在关闭和打开标准输出、“复制”、写入文件和读取文件以及其他奇怪的解决方案之后,我找到了一个适合我需要的解决方案,但不是那么便携但不需要另一个文件被打开:

How to buffer stdout in memory and write it from a dedicated thread

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

#define MAX_LEN 40

int main( int argc, char *argv[] ) {
  char buffer[MAX_LEN+1] = {0};
  int out_pipe[2];
  int saved_stdout;

  saved_stdout = dup(STDOUT_FILENO);  /* save stdout for display later */

  if( pipe(out_pipe) != 0 ) {          /* make a pipe */
    exit(1);
  }

  dup2(out_pipe[1], STDOUT_FILENO);   /* redirect stdout to the pipe */
  close(out_pipe[1]);

  /* anything sent to printf should now go down the pipe */
  printf("ceci n'est pas une pipe");
  fflush(stdout);

  read(out_pipe[0], buffer, MAX_LEN); /* read from pipe into buffer */

  dup2(saved_stdout, STDOUT_FILENO);  /* reconnect stdout for testing */
  printf("read: %s\n", buffer);

  return 0;
}

在重写它以创建一个自己的模块“stdout_redirect.c”后,结果是:

#define BIGENOUGH 1000
#define PIPE_READ   0
#define PIPE_WRITE  1

char stdout_buffer [BIGENOUGH];
static int stdout_save;
static int stdout_pipe[2];

bool stdout_redirect()
{
    stdout_save = dup(STDOUT_FILENO);  /* save stdout for display later */

    if( pipe(stdout_pipe) != 0 ) {          /* make a pipe */
        return false;
    }

    dup2(stdout_pipe[PIPE_WRITE], STDOUT_FILENO);   /* redirect stdout to the pipe */
    close(stdout_pipe[PIPE_WRITE]);

    return true;
}

ssize_t stdout_restore()
{
    ssize_t size;
    fflush(stdout); /* flush if not flushed before */
    size = read(stdout_pipe[PIPE_READ], stdout_buffer, sizeof(stdout_buffer)); /* read from pipe into buffer */
    close(stdout_pipe[PIPE_READ]);
    dup2(stdout_save, STDOUT_FILENO);  /* reconnect stdout */
    return size;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-28
    • 2013-10-27
    相关资源
    最近更新 更多