double d1 = str_to_double("12.4"); // проверка
double d2 = str_to_double("1.34e–3");
double d3 = str_to_double("twelve point three"); // вызывается
// error()
Если попытаться прочитать данные за пределами строки, предназначенной для ввода в поток
istringstream
, то он перейдет в состояние
eof()
. Это значит, что для потока
istringstream
можно использовать обычный цикл ввода; поток
istringstream
на самом деле является разновидностью потока
istream
.
Поток
ostringstream
, наоборот, может быть полезен для форматирования вывода в системах, ожидающих аргумента в виде простой строки, например в системах графического пользовательского интерфейса (раздел 16.5). Рассмотрим пример.
void my_code(string label, Temperature temp)
{
// ...
ostringstream os; // поток для составления сообщения
os << setw(8) << label << ": "
<< fixed << setprecision(5) << temp.temp << temp.unit;
someobject.display(Point(100,100), os.str().c_str());
// ...
}
Функция-член
str()
класса
ostringstream
возвращает объект класса
string
, составленный операторами вывода, в поток
ostringstream
. Функция
c_str()
— это функция-член класса
string
, возвращающая строки в стиле языка C, которые ожидаются интерфейсами многих систем.
Потоки
stringstream
обычно используются, когда мы хотим отделить собственно ввод-вывод от обработки данных. Например, аргумент типа
string
в функции
str_to_double()
обычно поступает из файла (например, из журнала событий веб) или с клавиатуры. Аналогично, сообщение, составленное функцией
my_code()
, в конце концов выводится на экран. Например, в разделе 11.7 мы используем поток
stringstream
при выводе для фильтрации нежелательных символов. Таким образом, потоки
stringstream
можно интерпретировать как механизм настройки ввода-вывода для особых потребностей и вкусов.
Продемонстрируем использование потока
ostringstream
на простом примере конкатенации строк.
int seq_no = get_next_number(); // вводим число из системного журнала
ostringstream name;
name << "myfile" << seq_no; // например, myfile17
ofstream logfile(name.str().c_str()); // например, открыть myfile17
Как правило, поток
istringstream
инициализируется объектом класса
string
, а затем считывает из него символы, используя операторы ввода. И наоборот, поток
ostringstream
инициализируется пустым объектом класса
string
, а затем заполняется с помощью операторов вывода. Существует более простой способ доступа к символам в потоке
stringstream
, который иногда оказывается полезным: функция
ss.str()
возвращает копию строки из объекта
ss
, а функция
ss.str(s)
присваивает строке в объекте
ss
копию строки
s
. В разделе 11.7 приведен пример, в котором функция
ss.str(s)
играет существенную роль.
11.5. Ввод, ориентированный на строки
Оператор
>>
вводит данные в объекты заданного типа в соответствии со стандартным форматом, установленным для этого типа. Например, при вводе чисел в объект типа
int
оператор
>>
будет выполнять ввод, пока не обнаружит символ, не являющийся цифрой, а при вводе в объект класса
string
оператор
>>
будет считывать символы, пока не обнаружит разделитель (whitespace). Стандартная библиотека istream содержит также средства для ввода отдельных символов и целых строк. Рассмотрим пример.
string name;
cin >> name; // ввод: Dennis Ritchie
cout << name << '\n'; // вывод: Dennis
Что, если мы захотим прочитать всю строку сразу, а способ ее форматирования выберем потом? Это можно сделать с помощью функции
getline()
. Рассмотрим пример.
string name;
getline(cin,name); // ввод: Dennis Ritchie
cout << name << '\n'; // вывод: Dennis Ritchie
Теперь мы считали целую строку. Зачем нам это было нужно? Например, неплохой ответ: “Потому что мы сделали то, чего не может оператор
>>
”. Часто можно слышать совершенно неудачное объяснение: “Потому что пользователь набрал полную строку”. Если это все, что вы можете сказать, то используйте оператор
>>
, потому что, если вы ввели строку, то должны как-то ее разобрать на части. Рассмотрим пример.
string first_name;
string second_name;
stringstream ss(name);
ss>>first_name; // ввод строки Dennis
ss>>second_name; // ввод строки Ritchie
Непосредственный ввод данных в строки
first_name
и
second_name
можно было бы выполнить проще. Одна из распространенных причин для считывания полной строки заключается в том, что определение разделителя не всегда является достаточно приемлемым. Иногда переход на новую строку желательно трактовать не как разделитель. Например, в ходе обмена сообщениями в компьютерной игре текст разумнее интерпретировать как предложение, не полагаясь на общепринятую пунктуацию.