0

我昨天问过这个问题,但我仍然有问题。我用 C 语言编写了一个程序,该程序具有处理文件的函数,该函数可以通过文件指针传递给它。

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

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

my_process_file(stdin, ...);

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

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

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

——拉里

4

3 回答 3

0

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

于 2009-09-27T22:59:10.190 回答
0

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

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

void process_data(char data[], size_t data_len) { /* do work */ }
于 2009-09-27T23:08:53.403 回答
0

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

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

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


这是一个工作示例程序。我必须想办法把整个文件塞进内存,处理它,然后给出一些答案——所以我选择了按字符对文件进行排序。适度无意义,但它演示了该做什么。它还有一个操作变量参数错误报告功能。

玩得开心!

/*
 * 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()可能失败的可能性;我也选择忽略 in 上溢出的buflen可能性process_my_file()。如果您愿意,可以检查它们。(请注意,每个文件的输出都比输入多一个换行符。)

读者练习:

  • 将不可打印的字符打印为 ''\xXX`' 转义序列。
  • 将输出分成每行不超过 64 个字符的行。
  • 设计或研究替代分配策略,例如将每次分配的空间加倍(参见“编程实践”)
于 2009-09-27T23:08:57.680 回答