Using istringstream in C++

余生颓废 提交于 2020-03-22 09:16:10

问题


I have some code that utilizes fork, execlp, and wait to make two processes. The objective is to be able to repeatedly print a prompt and have the user enter a command with up to 4 arguments/options to the command.

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

I knew that the current code I have would be able to support only one argument to the command, but I wasn't sure about what to use in order to specify between 1 to 4 arguments. That's when I a friend mentioned to me about std::istringstream, but while looking into it, I didn't understand how to use it for input with the rest of the program. Is there a way to set it up or is there a different method to use to fulfill the requirements?


回答1:


The most common usage pattern for std::istringstream with user input is to accept a single line of text and then process it. This avoids problems that can occur when the input does not match what you expect, or cannot be predicted.

Here is a simple example that reads one line at a time from STDIN, and processes it into a command followed by a vector of strings as arguments.

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

Of course, you don't need to read into strings, or store stuff in a vector. This was simply for illustrative purposes.

The basic point is to avoid pitfalls that people come up against, the most common of which is expecting the previous stream operation to have succeeded. When that assumption is false, the naive programmer can find themselves attempting to parse something that was supposed to be handed in the previous line.


Broken example:

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

In the above, let's say the user gets confused and "invokes" the foo command with one float argument, then the next command is a valid bar command:

foo 42.0
bar 3.14

Here's what happens:

  1. The foo handler reads arg1 as 42, then fails to read the next argument. The stream is now in error.

  2. No error checking was done on the input during that handler, so there's now undefined behavior outputting the value of arg2.

  3. When attempting to read the next command, the stream is already in an error state and so the loop terminates.

So, the output of that program might look like:

Got command: foo
foo: 42, -149017896

Fixing this problem without istringstream is possible, but it's a pain. There are many reasons why a stream might enter an error state, and clearing error flags for specific error states just to get around this problem makes for ugly and potentially error-prone code. Not only would you need to clear the error flags, but you would also need to tell the stream to ignore any remaining characters in the line. And you may have already started reading into the next line without knowing.


More robust example:

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

Now, the same input from the previous example will give the output:

Got command: foo
Got command: bar
bar: 3.14

The differences are:

  • Using istringstream, any errors processing our input did not affect the source stream, so a failure on the previous line does not cause problems.

  • The string stream is correctly checked when reading arguments, allowing optional error handling.

  • If, after some parsing failure, you decide to try processing a line of input differently, that's easy to do with another string stream.




回答2:


If I understand your assignment correctly, your apparent problem is here:

 cin >> command >> argument;

You need a string that holds the entire command line (getline?). Then you need an istringstream that is bound to the string holding the entire command line.

Then you need to do something like

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

At this point I am lost on your what your assignment's requirements. However, you might want to do something like:

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

for error checking. If arg5 is not an empty string, too many arguments have been specified.



来源:https://stackoverflow.com/questions/53491404/using-istringstream-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!