在 C++ 中使用 istringstream

Dra*_*els 3 c++ fork exec wait istringstream

我有一些代码使用 fork、execlp 和 wait 来创建两个进程。目标是能够重复打印提示并让用户输入命令,该命令最多包含 4 个参数/选项。

int main()
{
     string command, argument;
     istringstream iss(argument);

  do
  {
  //prompt user for command to run
  cout << "Enter command: ";
  cin >> command >> argument;

  int pid, rs, status;
 
  //fork will make 2 processes
  pid = fork();
  if(pid == -1) { perror("fork"); exit(EXIT_FAILURE); }

if(pid == 0) {
//Child process: exec to command with argument

//C_str to get the character string of a string
rs = execlp(command.c_str(), command.c_str(), argument.c_str(), (char*) NULL);
.
if (rs == -1) { perror("execlp"); exit(EXIT_FAILURE); }
} else {
   //Parent process: wait for child to end
   wait(&status);
      }
   } while(command != "exit");

   return 0;
}
Run Code Online (Sandbox Code Playgroud)

我知道我拥有的当前代码只能支持命令的一个参数,但我不确定使用什么来指定 1 到 4 个参数。那时我的朋友提到了std::istringstream,但是在研究它时,我不明白如何使用它来输入程序的其余部分。有没有办法设置它或者是否有不同的方法来满足要求?

pad*_*ddy 7

std::istringstreamwith 用户输入最常见的使用模式是接受单行文本,然后对其进行处理。这可以避免在输入与您期望的不匹配或无法预测时可能出现的问题。

这是一个简单的示例,它一次从 STDIN 读取一行,并将其处理为一个命令,后跟一个字符串向量作为参数。

for(std::string line; std::getline(std::cin, line); )
{
    std::istringstream iss(line);

    std::string command;
    if (iss >> command)
    {
        std::vector<std::string> args;
        for (std::string arg; iss >> arg; )
        {
            args.push_back(arg);
        }
        std::cout << "Received '" << command << "' with " << args.size() << " arguments\n";
    }
    else
    {
        std::cerr << "Invalid input" << std::endl;
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,您不需要读入字符串或将内容存储在向量中。这只是为了说明目的。

基本点是避免人们遇到的陷阱,其中最常见的是期望之前的流操作成功。当该假设为假时,天真的程序员会发现自己试图解析应该在前一行中传递的内容。


破碎的例子:

#include <iostream>

int main() {
    std::string command;
    while (std::cin >> command)
    {
        std::cout << "Got command: " << command << std::endl;
        if (command == "foo")
        {
            // 'foo' expects two integer arguments:
            int arg1, arg2;
            std::cin >> arg1 >> arg2;
            std::cout << command << ": " << arg1 << ", " << arg2 << std::endl;
        }
        else if (command == "bar")
        {
            // 'bar' expects one float argument:
            float arg1;
            std::cin >> arg1;
            std::cout << command << ": " << arg1 << std::endl;
        }
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

在上面,假设用户感到困惑并使用一个浮点参数“调用” foo命令,那么下一个命令是一个有效的bar命令:

foo 42.0
bar 3.14
Run Code Online (Sandbox Code Playgroud)

这是发生的事情:

  1. FOO处理器读取ARG1为42,则无法读取下一个参数。流现在出错了。

  2. 在该处理程序期间没有对输入进行错误检查,因此现在有未定义的行为输出arg2.

  3. 当尝试读取下一个命令时,流已经处于错误状态,因此循环终止。

因此,该程序的输出可能如下所示:

Got command: foo
foo: 42, -149017896
Run Code Online (Sandbox Code Playgroud)

解决这个问题istringstream是可能的,但这很痛苦。流可能进入错误状态的原因有很多,为了解决这个问题而清除特定错误状态的错误标志会产生丑陋且可能容易出错的代码。您不仅需要清除错误标志,还需要告诉流忽略该行中的任何剩余字符。您可能已经开始在不知情的情况下阅读下一行。


更健壮的例子:

#include <iostream>
#include <sstream>

int main() {
    std::string command;
    for (std::string line; std::getline(std::cin, line); )
    {
        std::istringstream iss(line);
        if (!(iss >> command))
            continue;
        std::cout << "Got command: " << command << std::endl;
        if (command == "foo")
        {
            // 'foo' expects two integer arguments:
            int arg1, arg2;
            if (iss >> arg1 >> arg2)
            {
                std::cout << command << ": " << arg1 << ", " << arg2 << std::endl;
            }
        }
        else if (command == "bar")
        {
            // 'bar' expects one float argument:
            float arg1;
            if (iss >> arg1)
            {
                std::cout << command << ": " << arg1 << std::endl;
            }
        }
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

现在,来自上一个示例的相同输入将给出输出:

Got command: foo
Got command: bar
bar: 3.14
Run Code Online (Sandbox Code Playgroud)

区别在于:

  • 使用istringstream,处理我们输入的任何错误都不会影响源流,因此前一行的失败不会导致问题。

  • 读取参数时正确检查字符串流,允许可选的错误处理。

  • 如果在某些解析失败后,您决定尝试以不同方式处理输入行,那么使用另一个字符串流很容易做到。