Использование istringstream в C ++ - PullRequest
0 голосов
/ 27 ноября 2018

У меня есть некоторый код, который использует 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, но, глядя на него, я не понимал, как использовать его для ввода с остальной частью программы.Есть ли способ настроить его или использовать другой метод для удовлетворения требований?

Ответы [ 2 ]

0 голосов
/ 27 ноября 2018

Наиболее распространенный шаблон использования для 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, любые ошибкиобработка нашего ввода не повлияла на исходный поток, поэтому сбой в предыдущей строке не вызывает проблем.

  • Поток строки корректно проверяется при чтении аргументов, что допускает необязательную обработку ошибок.

  • Если после некоторого сбоя синтаксического анализа вы решите попробовать обработать строку ввода по-другому, это легко сделать с другим потоком строк.

0 голосов
/ 27 ноября 2018

Если я правильно понимаю ваше назначение, ваша очевидная проблема здесь:

 cin >> command >> argument;

Вам нужна строка, содержащая всю командную строку (getline?).Затем вам нужен istringstream, который связан со строкой, содержащей всю командную строку.

Тогда вам нужно сделать что-то вроде

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

В этот момент я теряюсь в соответствии с вашими требованиями.Тем не менее, вы можете сделать что-то вроде:

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

для проверки ошибок.Если arg5 не является пустой строкой, было указано слишком много аргументов.

...