Наиболее распространенный шаблон использования для 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
Вот что происходит:
Обработчик foo читает arg1 как 42, затем не может прочитать следующий аргумент,Поток теперь находится в состоянии ошибки.
На входе этого обработчика проверка ошибок не производилась, поэтому теперь при выводе значения arg2
.
При попытке прочитать следующую команду поток уже находится в состоянии ошибки и цикл завершается.
Таким образом, выходные данные этой программы могут выглядеть следующим образом:
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
, любые ошибкиобработка нашего ввода не повлияла на исходный поток, поэтому сбой в предыдущей строке не вызывает проблем.
Поток строки корректно проверяется при чтении аргументов, что допускает необязательную обработку ошибок.
Если после некоторого сбоя синтаксического анализа вы решите попробовать обработать строку ввода по-другому, это легко сделать с другим потоком строк.