istringstream is(s);

  T t;

  if (!(is >> t)) throw bad_from_string();

  return t;

}

Рассмотрим пример.

double d = from_string("12.333");

void do_something(const string& s)

try

{

  int i = from_string(s);

  // ...

}

catch (bad_from_string e) {

  error ("Неправильная строка ввода",s);

}

Дополнительная сложность функции from_string() по сравнению с функцией to_string() объясняется тем, что класс string может представлять значения многих типов. Это значит, что каждый раз мы должны указывать, какой тип значений хотим извлечь из объекта класса string. Кроме того, это значит, что класс string, который мы изучаем, может не хранить значение типа, который мы ожидаем. Рассмотрим пример.

int d = from_string("Mary had a little lamb"); // Ой!

Итак, возможна ошибка, которую мы представили в виде исключения типа bad_from_string. В разделе 23.9 мы покажем, что функция from_string() (или эквивалентная) играет важную роль в серьезных текстовых приложениях, поскольку нам необходимо извлекать числовые значения из текстовых полей. В разделе 16.4.3 было показано, как эквивалентная функция get_int() используется в графическом пользовательском интерфейсе.

Обратите внимание на то, что функции to_string() и from_string() очень похожи. Фактически они являются обратными друг другу; иначе говоря (игнорируя детали, связанные с пробелами, округлением и т.д.), для каждого “разумного типа T” имеем

s==to_string(from_string(s)) // для всех s

и

t==from_string(to_string(t)) // для всех t

Здесь слово “разумный” означает, что тип T должен иметь конструктор по умолчанию, оператор >> и соответствующий оператор <<.

  Следует подчеркнуть, что реализации функций to_string() и from_string() используют класс stringstream для выполнения всей работы. Это наблюдение было использовано для определения универсальной операции конвертирования двух произвольных типов с согласованными операциями << и >>.

struct bad_lexical_cast:std::bad_cast

{

  const char* what() const { return "bad cast"; }

};

template

Target lexical_cast(Source arg)

{

  std::stringstream interpreter;

  Target result;

  if (!(interpreter << arg)        // записываем arg в поток

      || !(interpreter >> result)  // считываем result из потока

      || !(interpreter >> std::ws).eof()) // поток пуст?

         throw bad_lexical_cast();

  return result;

}

Довольно забавно и остроумно, что инструкция !(interpreter>>std::ws).eof() считывает любой пробел, который может остаться в потоке stringstream после извлечения результата. Пробелы допускаются, но кроме них в потоке ввода может не остаться никаких других символов, и мы должны реагировать на эту ситуацию, как на обнаружение конца файла. Итак, если мы пытаемся считать целое число int из объекта класса string, используя класс lexical_cast, то в результате выражения lexical_cast("123") и lexical_cast("123") будут считаться допустимыми, а выражение lexical_cast("123.5") — нет из-за последней пятерки.

Довольно элегантное, хотя и странное, имя lexical_cast используется в библиотеке boost, которую мы будем использовать для сравнения регулярных выражений в разделах 23.6–23.9. В будущем она станет частью новых версий стандарта языка С++ .

<p id="AutBody_Root444"><strong>23.3. Потоки ввода-вывода</strong></p>

  Рассматривая связь между строками и другими типами, мы приходим к потокам ввода-вывода. Библиотека ввода-вывода не просто выполняет ввод и вывод, она осуществляет преобразования между форматами и типами строк в памяти. Стандартные потоки ввода-вывода обеспечивают возможности для чтения, записи и форматирования строк символов. Библиотека iostream описана в главах 10-11, поэтому просто подведем итог.

Стандартные потоки организованы в виде иерархии классов (см. раздел 14.3).

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже