从文件中获取数据并在管道中移动

时间:2013-02-20 20:30:18

标签: c linux unix io ipc

免责声明:这是作业。我不是要求明确的代码。我只是要求帮助理解这些概念,以便我可以在不复制别人所做的事情的情况下学习这些材料。

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

所以我将main函数分为三个进程:input / parent,sort(child)和output(grandchild)。我在父进程中创建了一个IO流,需要将它传递给sort proc。我创建了该管道并关闭了未使用的末尾(stdin用于输入,stdout用于排序。)

我的问题是:我不知道如何将数据从IO缓冲区移动到管道中(在stdout处)。我觉得它应该使用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++;
    }
}

2 个答案:

答案 0 :(得分:2)

您的问题是:

  

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

从我收集的内容来看,你过度思考问题。如果您知道如何从FILE *读取并写入FILE *,那么管道没有什么特别之处。

我会像这样构建程序:

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

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

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需要映射到stdin,而outfile需要映射到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个流程参与,我会考虑将sortunique拆分为sortunique

    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

答案 1 :(得分:0)

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

如果您使用fscanf()进行阅读,可以使用fprintf()进行书写。