从管道中创建(或传递)字符串

Creating (or passing) a string from a pipe

本文关键字:字符串 管道 创建      更新时间:2023-10-16

好吧,我正在做一个"简单"的项目,使用fork和piping在文件中搜索给定单词的出现次数。该程序将以父级一次向子级发送一个单词的方式分叉该过程,然后子级搜索文件并总结所传递单词的出现次数。我的问题是我不熟悉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;
}

测试

我发送的单词文件是比利·莎士比亚在《随心所欲》第二幕第七场中的独白。输出的开始和结束如下所示:

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

// 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();
}
};

利用这一点,来自上一个源列表的客户端进程代码段可以简单地适应以下内容:

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以缓冲不止一个字符(一些额外的内务处理内容),但我把它留给你去发现。