Читать интересную книгу Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 118 119 120 121 122 123 124 125 126 ... 337
class="code">  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 можно было бы выполнить проще. Одна из распространенных причин для считывания полной строки заключается в том, что определение разделителя не всегда является достаточно приемлемым. Иногда переход на новую строку желательно трактовать не как разделитель. Например, в ходе обмена сообщениями в компьютерной игре текст разумнее интерпретировать как предложение, не полагаясь на общепринятую пунктуацию.

идти налево, пока не увидишь картину справа на стене

сними картину со стены и открой дверь позади нее. Возьми сундук

В данном случае мы сначала прочитаем всю строку, а затем извлечем из нее отдельные слова.

string command;

getline(cin,command);             // вводим строку

stringstream ss(command);

vector<string> words;

string s;

while (ss>>s) words.push_back(s); // извлекаем отдельные слова

С другой стороны, если есть выбор, то лучше всего ориентироваться на знаки пунктуации, а не на символ перехода на новую строку. 

11.6. Классификация символов

Как правило, мы вводим целые числа, числа с плавающей точкой, слова и так далее, в соответствии с общепринятым форматом. Однако мы можем, а иногда и должны, снизить уровень абстракции и ввести отдельные символы. Для этого необходимо затратить больше усилий, но, считывая отдельные символы, мы получаем полный контроль на тем, что делаем. Рассмотрим задачу распознавания лексем в выражениях из раздела 7.8.2.

Допустим, мы хотим разделить выражение 1+4*x<=y/z*5 на одиннадцать лексем.

1 + 4 * x <= y / z * 5

Для ввода чисел мы могли бы использовать оператор >>, но, пытаясь ввести идентификаторы как строки, должны были бы прочитать фразу x<=y как целую строку (поскольку символы < и = не являются разделителями). Сочетание символов z* мы также должны были бы ввести как целую строку (поскольку символ * также не является разделителем).

Вместо этого можно сделать следующее:

char ch;

while (cin.get(ch)) {

  if (isspace(ch)) { // если символ ch является разделителем,

                     // ничего не делаем (так как разделители

                     // игнорируются)

  }

  if (isdigit(ch)) {

                     // вводим число

  }

  else if (isalpha(ch)) {

                     // вводим идентификатор

  }

1 ... 118 119 120 121 122 123 124 125 126 ... 337
На этом сайте Вы можете читать книги онлайн бесплатно русская версия Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп.
Книги, аналогичгные Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп

Оставить комментарий