好吧,我正在一个使用 fork 和管道来搜索文件中给定单词出现次数的“简单”项目。该程序将以这样一种方式 fork 该过程,即 parent 一次向 child 发送一个单词,让 child 随后搜索文件并总结所传递单词的出现情况。我的问题是我不熟悉C++,因此很难弄清楚如何从管道中获取字符串。该程序当前正在通过管道传递单词,但是它们在运行时仅作为一长行字符出现。谁能提供一些示例或技巧来检索字符串而不是单个字符?到目前为止,这是我的代码:

int main (int argc, char * argv[]) {

fstream fileWell ("well.txt");
fstream fileWords ("words.txt");

int pipefd[2];
int counter = 0;
pid_t cpid;
char buf;
const char * sentWord;
string toCheck;
string toSend;

pipe(pipefd);
cpid = fork();

if (cpid == -1) {
    cout << "ERROR" << endl;
    exit(1);
}

if (cpid == 0) {
    close(pipefd[1]);
        while (read(pipefd[0], &buf, 1) > 0)
            write(1, &buf, 1);
        cout << endl;
        write(1, "\n", 1);
}

else {
    while (getline(fileWords, toSend)) {
        close(pipefd[0]);
        sentWord = toSend.c_str();
        write(pipefd[1], sentWord, strlen(sentWord));
    }
    close(pipefd[0]);
    toSend = "-1";
    sentWord = toSend.c_str();
    write(pipefd[1], sentWord, 3);
    close(pipefd[1]);
    wait(NULL);
    exit(EXIT_SUCCESS);
}
return 0;
}

我觉得一旦知道字符串,我就知道该怎么办,但是如果没有那一部分,我将无法真正前进。感谢您的任何建议或帮助。

最佳答案

取出未使用的数据并仅专注于功能代码的目的,我可以肯定地说,以下是您至少要实现的目标。解决了一些问题。

  • 除非仅在父进程上,否则不会打开输入文件流。
  • 修复了管道手柄上的多个闭合。
  • 使用std:string成员进行数据指针和长度计算
  • 确保字符串终止符作为数据包的一部分发送。
  • 将子级上的终止符视为完成字符串的信号。

  • 您对接收到的单词的处理取决于您自己。我对此进行了修改,以便在重置之前将其发送到标准输出。我认为您可能有替代计划,但这与问题无关。

    见下文。根据需要进行调整:
    #include <cstdlib>
    #include <iostream>
    #include <fstream>
    #include <string>
    #include <unistd.h>
    using namespace std;
    
    #define READ_FD     0
    #define WRITE_FD    1
    
    int main (int argc, char * argv[])
    {
        int fd[2];
        pid_t cpid;
    
        pipe(fd);
        if ((cpid = fork()) == -1)
        {
            cout << "ERROR" << endl;
            exit(1);
        }
    
        // child process
        if (cpid == 0)
        {
            // don't need the write-side of this
            close(fd[WRITE_FD]);
    
            std::string s;
            char ch;
            while (read(fd[READ_FD], &ch, 1) > 0)
            {
                if (ch != 0)
                    s.push_back(ch);
                else
                {
                    std::cout << s << '\n';
                    s.clear();
                }
            }
    
            // finished with read-side
            close(fd[READ_FD]);
        }
    
        // parent process
        else
        {
            // don't need the read-side of this
            close(fd[READ_FD]);
    
            fstream fileWords ("words.txt");
            string toSend;
            while (fileWords >> toSend)
            {
                // send word including terminator
                write(fd[WRITE_FD], toSend.c_str(), toSend.length()+1);
            }
    
            // finished with write-side
            close(fd[WRITE_FD]);
            wait(NULL);
        }
        return EXIT_SUCCESS;
    }
    

    测试

    我通过此发送的文字文件是比利·莎士比亚(Billy Shakespeare)的独白,摘自《第二幕,第七幕》。输出的开始和结束如下所示:
    All
    the
    worlds
    a
    stage
    And
    all
    the
    men
    and
    women
    merely
    players
    ....
    oblivion
    Sans
    teeth
    sans
    eyes
    sans
    taste
    sans
    everything
    

    替代方案:自定义流缓冲区

    另一种选择(也许是您真正要寻找的)是调整可与std::istream结合使用的流缓冲区,以类似于常规流io在客户端使用。我可以召集的最简单的示例是一个字符缓冲区的streambuf,如下所示:
    // adapt a single char streambuf for an input stream
    class ifd_streambuf : public std::streambuf
    {
    protected:
        int  d_fd;
        char d_buffer[1];
    
    public:
        ifd_streambuf(int fd) : d_fd(fd)
        {
            setg(d_buffer, d_buffer + 1, d_buffer + 1);
        };
    
    private:
        int underflow()
        {
            if (read(d_fd, d_buffer, 1) <= 0)
                return EOF;
    
            setg(d_buffer, d_buffer, d_buffer + 1);
            return *gptr();
        }
    };
    

    利用这一点,可以将来自先前源代码 list 的客户端过程代码段修改为简单地遵循以下内容:
    ifd_streambuf fdbuf(fd[READ_FD]);
    std::istream inf(&fdbuf);
    
    std::string s;
    while (inf >> s)
        std::cout << s << '\n';
    

    这比您可能习惯的C++风格要多得多。服务器端也需要进行更改,将任何空格附加为单词分隔符:
    while (fileWords >> toSend)
    {
        write(fd[WRITE_FD], toSend.c_str(), toSend.length());
        write(fd[WRITE_FD], " ", 1);
    }
    

    要使此streambuf缓冲多于一个字符(需要一些额外的内部管理工作),需要付出额外的工作,但我留给您发现。

    10-07 12:03
    查看更多