在C++中使用istringstream

4

我有一些代码,利用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,但是在研究它时,我不知道如何将其与程序的其他部分一起使用。是否有一种方法来设置它或者有其他方法可以满足要求?


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

9

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修复此问题是可能的,但很麻烦。流可能进入错误状态的原因有很多,只为了解决这个问题而清除特定错误状态的错误标志会导致丑陋且可能容易出错的代码。你不仅需要清除错误标志,还需要告诉流忽略行中剩余的任何字符。而且你可能已经在不知情的情况下开始读取下一行了。


更健壮的示例:

#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,任何处理输入时的错误都不会影响源流,因此前一行的失败不会引起问题。

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

  • 如果在某些解析失败后,您决定尝试以不同方式处理输入行,则可以使用另一个字符串流轻松实现。


第一个例子假设每个参数都在单独的一行上? - mLstudent33
1
不,每个“命令”都是单独的一行,并且可以有多个参数。 - paddy

1
如果我正确理解您的任务,您的明显问题在这里:
 cin >> command >> argument;

你需要一个字符串来保存整个命令行(getline?)。然后你需要一个与保存整个命令行的字符串绑定的istringstream。
然后你需要做一些像这样的事情:
iss >> command >> arg1 >> arg2 >> arg3 >> arg4 ;

此时,我对你的任务要求感到困惑。不过,你可能想做一些类似于以下的事情:
iss >> command >> arg1 >> arg2 >> arg3 >> arg4 >> arg5 ;

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接