3

免责声明:这是一个任务。我不是要求明确的代码。我只是寻求帮助理解这些概念,以便我可以学习材料而不复制其他人所做的事情。

我的任务是从文本文件中获取一些数据,解析出单词,按字母顺序排序,删除重复项,然后将它们输出到一个新的文本文件中。这将使用三个进程来完成,使用 IPC 管道。这是我第一次与管道或任何 IPC 一起工作,所以如果我遇到一些外行,请理解。

所以我把主函数分成了三个进程:输入/父进程、排序(子进程)和输出(孙子进程)。我在父进程中创建了一个 IO 流,需要将其通过管道传输到排序过程。我已经创建了那个管道并关闭了未使用的末端(stdin用于输入,stdout用于排序。)

我的问题是:我不知道如何将数据从 IO 缓冲区移动到管道(在标准输出)。我觉得它应该使用fputs,其中一次将解析的单词列表传递给stdout一个单词。

以下是我迄今为止为输入过程创建的内容。文件 IO 不是我的强项,所以如果那里有错误,请告诉我,我会尽我所能修复它们。谢谢您的帮助!

} else {                            /* This is the input/parent process */

    printf("This is the input process\n");

    close(input_to_sortFD[0]);      /*  
                                     * Closes the parent-side read-end of
                                     * the pipe 
                                     */

    pipeStream = fdopen(input_to_sortFD[1], "w"); /* 
                                                   * Buffer that feeds into
                                                   * write-end of pipe
                                                   */


    ioFileFD = fopen(ioFile, "r");
    if (ioFileFD == NULL) {
        perror("Fatal error: failed to open requested file");
        exit(-1);
    }

    int i = 0;

    while (fscanf(ioFileFD, "%s", wordList) != EOF) {
        fputs(wordList[i], stdout);
        i++;
    }
}
4

2 回答 2

2

你的问题是:

我不知道如何将数据从 IO 缓冲区移动到管道中(在标准输出处)。

从我收集到的信息来看,您对问题的思考过度了。如果您知道如何从 a 读取FILE *和写入 a FILE *,那么管道就没有什么特别之处了。

我会像这样构建程序:

  • 父进程
    • 打开输入和输出文件,创建管道
    • 启动子进程“wordify”
    • 启动子进程“sortunique”
    • 等待孩子完成
  • 词化过程
    • 读取输入并写入单词
  • 排序独特的过程
    • 读取单词,对出现的唯一事件进行排序,并输出结果列表

sed您可以分别使用和对“wordify”和“sortunique”过程进行原型设计sort。下面的原型将单词定义为连续出现的字母字符。

void wordify (FILE *infile, FILE *outfile)
{
    int r;
    make_stdio(infile, outfile);
    r = execlp("sed", "sed", "-e",
               "s/[^a-zA-Z][^a-zA-Z]*/ /g;s/^ *//;s/ *$//;/^$/d;s/ /\\n/g",
               (char *)0);
    assert(r == 0);
}

void sortunique (FILE *infile, FILE *outfile)
{
    int r;
    make_stdio(infile, outfile);
    r = execlp("sort", "sort", "-u", (char *)0);
    assert(r == 0);
}

由于原型使用execlp()infile需要映射到stdinoutfile需要映射到stdout。这是用 完成的dup2(),但我实现了一个fdup2()处理FILE *.

FILE * fdup2 (FILE *oldstream, FILE *newstream)
{
    if (newstream) {
        if (fileno(oldstream) != fileno(newstream)) {
            if (dup2(fileno(oldstream), fileno(newstream)) < 0) return 0;
            fclose(oldstream);
        }
        return newstream;
    }
    return oldstream;
}

void make_stdio (FILE *infile, FILE *outfile)
{
    FILE *x = fdup2(infile, stdin);
    FILE *y = fdup2(outfile, stdout);
    assert(x && y);
}

进程fork()按预期启动。

void launch (void (*func)(FILE *, FILE *), FILE *infile, FILE *outfile)
{
    assert(infile && outfile);
    switch (fork()) {
    case -1: perror("fork");
             exit(EXIT_FAILURE);
    case 0:  func(infile, outfile);
             exit(EXIT_SUCCESS);
    default: fclose(infile);
             fclose(outfile);
    }
}

现在,主程序只需要打开输入和输出文件,创建管道,启动进程,然后等待它们完成。唯一的技巧是必须使用管道,以便它的写入端是 的输出wordify,而它的读取端是 的输入sortunique

int main (int argc, char *argv[])
{
    FILE *infile;
    FILE *outfile;
    int pipefds[2];
    int r;

    if (argc < 3) {
        fprintf(stderr, "need input and output filenames\n");
        exit(EXIT_FAILURE);
    }

    if ((infile = fopen(argv[1], "r")) == 0) {
        perror(argv[1]);
        exit(EXIT_FAILURE);
    }

    if ((outfile = fopen(argv[2], "w")) == 0) {
        perror(argv[2]);
        exit(EXIT_FAILURE);
    }

    r = pipe(pipefds);
    assert(r == 0);

    launch(wordify, infile, fdopen(pipefds[1], "w"));
    launch(sortunique, fdopen(pipefds[0], "r"), outfile);
    while (waitpid(-1, 0, 0) == 0) {}

    return 0;
}

注意,总共有 3 个进程参与。如果您必须启动 3 个,并且有 4 个进程参与,我会考虑拆分sortuniquesortunique

    r = pipe(pipefds1);
    assert(r == 0);
    r = pipe(pipefds2);
    assert(r == 0);

    launch(wordify, infile, fdopen(pipefds1[1], "w"));
    /* sort: behaves like "sort" command with no arguments */
    launch(sort, fdopen(pipefds1[0], "r"), fdopen(pipefds2[1], "w"));
    /* unique: behaves like "uniq" command with no arguments */
    launch(unique, fdopen(pipefds2[0], "r"), outfile);
    while (waitpid(-1, 0, 0) == 0) {}

一旦您将问题分解为这些组件,那么实现特定组件就只是一个标准的读取输入和写入输出练习。管道问题被抽象为按正确顺序排列的文件流,组件只是读写。您甚至可以删除make_stdio()代码,然后读取infile并写入outfile.

于 2013-02-25T05:27:03.223 回答
0

通常,管道通信是通过 read() 和 write() 调用完成的。但是我看到您正在将 int 文件描述符提升为 FILE* 以便您可以使用 fscanf 和朋友。

如果您使用 fscanf() 进行读取,则可以使用 fprintf() 进行写入。

于 2013-02-20T23:44:00.760 回答