【问题标题】:Using istringstream in C++在 C++ 中使用 istringstream
【发布时间】:2019-04-28 16:56:08
【问题描述】:

我有一些代码使用 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;
}

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

【问题讨论】:

  • 如果您不确定如何使用istringstream,为什么不删除所有多线程内容并编辑您的问题以包含minimal reproducible example
  • 也许它不适用。应该如何使用这 1 到 4 个用户参数?
  • 它使用第一个参数读取命令,然后使用相同的命令但下一个参数重复该过程,依此类推。因此,如果我有一个输入,例如“touch dr/one dr/more dr/file”,它应该在 dr 目录下创建一个名为“one”的新文件,然后是另一个名为“more”的文件,以及另一个名为“文件”,都在同一个目录下。这将是一个具有多个参数的示例。

标签: c++ fork exec wait istringstream


【解决方案1】:

std::istringstream 最常见的用户输入使用模式是接受单行文本然后处理它。这样可以避免输入与预期不符或无法预测时可能出现的问题。

这是一个简单的示例,它一次从 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;
    }
}

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

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


破例:

#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;
}

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

foo 42.0
bar 3.14

会发生什么:

  1. foo 处理程序将 arg1 读取为 42,然后无法读取下一个参数。流现在出错了。

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

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

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

Got command: foo
foo: 42, -149017896

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


更强大的示例:

#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;
}

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

Got command: foo
Got command: bar
bar: 3.14

区别在于:

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

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

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

【讨论】:

  • 第一个例子假设每个 arg 都在一个单独的行上?
  • 不,每个命令都是单独的一行,可以有多个参数。
【解决方案2】:

如果我正确理解了你的任务,那么你明显的问题就在这里:

 cin >> command >> argument;

您需要一个包含整个命令行的字符串(getline?)。然后,您需要一个绑定到包含整个命令行的字符串的 istringstream。

然后你需要做类似的事情

iss >> command >> arg1 >> arg2 >> arg3 >> arg4 ;

在这一点上,我不知道你的任务要求是什么。但是,您可能想要执行以下操作:

iss >> command >> arg1 >> arg2 >> arg3 >> arg4 >> arg5 ;

用于错误检查。如果 arg5 不是空字符串,则指定的参数过多。

【讨论】:

    猜你喜欢
    • 2011-02-15
    • 2017-08-23
    • 1970-01-01
    • 2012-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多