将标准输入的输入传递给函数时进行缓冲

时间:2009-09-27 22:52:31

标签: c function arguments buffering

我问了这个yesterday,但我仍然遇到问题。我在C中编写了一个程序,它有一个处理可以通过文件指针传递给它的文件的函数。

void process_my_file(FILE *fptr, ...) {
    /* do work */
}

我问如何从标准输入读取输入并将其传递给我的函数,我建议尝试使用stdin作为参数调用函数:

my_process_file(stdin, ...);

这样可行,但我真正想做的是从stdin读取直到遇到EOF,然后立即将所有输入传递给函数。将stdin作为参数传递的问题是,每次用户输入一行输入并按下“enter”时,程序就会过早地吐出相应的输出行。

我希望输入和输出能够清晰分离,这样只有在用户说出EOF(Control-d)后输出才会出现。

提前再次感谢。我是一个学习编程的新手,你的提示是一个很大的帮助。我非常感谢这个网站。

- 拉里

3 个答案:

答案 0 :(得分:0)

你必须自己进行预缓冲,即读取stdin直到看到EOF,然后将一个长字符串(可能是\ n分隔线的常量)传递给你的函数。或者你的预缓冲读取例程可以分配一个指向分配行的char *数组。或者您的预缓冲例程将解析stdin并返回预处理的信息。取决于您想要对信息做什么。

答案 1 :(得分:0)

你现在拥有的是'filter'。过滤器是很棒的程序,但当然不适用于所有情况。无论如何,看看你是否可以让你的程序作为过滤器工作。

如果你真的必须在处理之前读取所有输入,你必须在某处保存输入,并且用FILE*调用处理函数是没有意义的(FILE *中的所有数据都已经被读);你可以将所有输入读入一个char数组并将该数组传递给你的函数。

void process_data(char data[], size_t data_len) { /* do work */ }

答案 2 :(得分:0)

假设您要打开一个文件,然后将文件句柄传递给您的函数。您在函数中的代码仍然必须在该常规文件上读取EOF。此外,它必须处理分配足够的空间来存储文件,并处理短读取。

所有这些只是stdin必须处理的同一组问题 - 唯一可能的区别是来自终端的stdin会为每行输入提供简短的读取,而每次从管道读取都会给出你读管道缓冲区的大小(或原子写入小于缓冲区大小),普通磁盘文件通常只会给你一个文件的最后一个块的简短读取。由于您的函数无法提前知道需要多少空间(当然不是管道或终端输入),您必须准备好处理动态内存分配 - malloc()realloc()

另外,如果你的函数期望获取已经为它读取的数据,为什么它传递文件句柄(FILE指针)而不是字符缓冲区及其长度?当您需要函数使用它时,您可以将文件句柄传递给函数 - 从可读句柄读取,或写入可写句柄(如果句柄打开以进行读写,则只是偶尔)。 / p>


这是一个有效的示例程序。我必须找出一些需要将整个文件粘贴到内存中,处理它并发出一些答案的东西 - 所以我选择按字符对文件进行排序。中等无意义,但它演示了该怎么做。它还有一个操作变量参数错误报告功能。

玩得开心!

/*
 * Demo code for StackOverflow question 1484693
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>

static char *arg0;

static void error(const char *fmt, ...)
{
    va_list args;
    int errnum = errno;  /* Catch errno before it changes */

    fprintf(stderr, "%s: ", arg0);
    va_start(args, fmt);
    vfprintf(stderr, fmt, args);
    va_end(args);
    if (errnum != 0)
        fprintf(stderr, " (%d: %s)", errnum, strerror(errnum));
    fputc('\n', stderr);
    exit(1);
}

static int char_compare(const void *v1, const void *v2)
{
    char c1 = *(const char *)v1;
    char c2 = *(const char *)v2;
    if (c1 < c2)
        return -1;
    else if (c1 > c2)
        return +1;
    else
        return 0;
}

static void process_my_file(FILE *fp)
{
    char   *buffer;
    size_t  buflen = 1024;
    size_t  in_use = 0;
    ssize_t nbytes;

    if ((buffer = malloc(buflen)) == 0)
        error("out of memory - malloc()");

    while ((nbytes = fread(buffer + in_use, sizeof(char), buflen - in_use, fp)) > 0)
    {
        if (nbytes < 0)
            error("error from fread()");
        in_use += nbytes;
        if (in_use >= buflen)
        {
            char *newbuf;
            buflen += 1024;
            if ((newbuf = realloc(buffer, buflen)) == 0)
                error("out of memory - realloc()");
            buffer = newbuf;
        }
    }

    /* Consistency - number/size vs size/number! */
    qsort(buffer, in_use, sizeof(char), char_compare);
    fwrite(buffer, sizeof(char), in_use, stdout);
    putchar('\n');

    free(buffer);
}

int main(int argc, char **argv)
{
    arg0 = argv[0];

    if (argc > 1)
    {
        for (int i = 1; i < argc; i++)
        {
            FILE *fp;
            if ((fp = fopen(argv[i], "r")) == 0)
                error("failed to open file %s", argv[i]);
            process_my_file(fp);
            fclose(fp);
        }
    }
    else
        process_my_file(stdin);
    return(0);
}

您可以使用一个或多个文件名作为参数来调用它;每个文件名都单独排序。你可以把东西塞进去;你可以从标准输入中读取它。 我选择忽略fwrite()fclose()可能失败的可能性;我还选择忽略buflenprocess_my_file()溢出的可能性。如果您愿意,可以查看它们。 (请注意,每个文件的输出包含比输入更多的换行符。)

读者练习:

  • 将不可打印的字符打印为''\ xXX`'转义序列。
  • 将输出分成不超过64个字符的行。
  • 设计或研究替代分配策略,例如将每个分配的空间加倍(参见“The Practice of Programming”)