C 将输入从标准输入传递到函数时的缓冲

C 将输入从标准输入传递到函数时的缓冲,c,function,arguments,buffering,C,Function,Arguments,Buffering,我问过这件事,但我仍然有问题。我用C写了一个程序,它有一个处理文件的函数,这些文件可以通过文件指针传递给它 void process_my_file(FILE *fptr, ...) { /* do work */ } 我询问如何从标准输入读取输入并将其传递给我的函数,建议我尝试使用stdin作为参数调用函数: my_process_file(stdin, ...); 这是可行的,但我真正想做的是从stdin读取数据,直到遇到EOF,然后将所有输入一次传递给函数。将stdin作为参数

我问过这件事,但我仍然有问题。我用C写了一个程序,它有一个处理文件的函数,这些文件可以通过文件指针传递给它

void process_my_file(FILE *fptr, ...) {
    /* do work */
}
我询问如何从标准输入读取输入并将其传递给我的函数,建议我尝试使用stdin作为参数调用函数:

my_process_file(stdin, ...);
这是可行的,但我真正想做的是从stdin读取数据,直到遇到EOF,然后将所有输入一次传递给函数。将stdin作为参数传递的问题是,每次用户输入一行输入并按enter键时,程序都会过早地输出相应的一行输出

我希望输入和输出能够完全分离,这样只有在用户说了EOF Control-d之后才能输出

提前再次感谢。我是一个学习编程的新手,你的建议对我帮助很大。我真的很感激这个网站


-Larry

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

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

如果您真的必须在处理之前读取所有输入,那么您必须将输入保存在某个位置,并且,使用文件*调用处理函数没有意义*文件*中的所有数据*都已读取;您可以将所有输入读入char数组,并将该数组传递给函数

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

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

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

另外,如果您的函数希望获取已经为其读取的数据,那么为什么传递给它的是文件句柄文件指针,而不是字符缓冲区及其长度?当需要函数使用文件句柄时,可以将文件句柄传递给该函数—从可读句柄读取,或向可写句柄写入,偶尔,如果句柄打开进行读取和写入,则两者都可以

下面是一个工作示例程序。我必须找出一些需要将整个文件存储在内存中的东西,对其进行处理,并给出一些答案——因此我选择按字符对文件进行排序。有点毫无意义,但它说明了该怎么做。它还具有操作变量参数错误报告功能

玩得开心

/*
 * 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可能失败的可能性;我还选择忽略进程\我的\文件中buflen溢出的可能性。如果您愿意,您可以检查它们。请注意,每个文件的输出比输入多包含一个换行符

读者练习:

将不可打印字符打印为\xXX`'转义序列。 将输出分成不超过64个字符的行。 设计或研究其他分配策略,例如将每次分配的空间增加一倍,请参见
也传入长度-以避免缓冲区溢出并处理数据中嵌入的null“\0”。如果数据“允许”具有嵌入的nul,我将使用无符号字符数据[],而不是纯字符数据[]。长度总是很好。