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

Значение строки s1 равно "12.333", а значение строки s2 — "17". Фактически функцию to_string можно применять не только к числовым значениям, но и к любому классу T с оператором << .

Обратное преобразование, из класса string в число, так же просто, как и полезно.

struct bad_from_string:std::bad_cast

  // класс для сообщений об ошибках при преобразовании строк

{

  const char* what const // override bad_cast’s what

  {

    return "bad cast from string";

  }

};

template T from_string(const string& s)

{

  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></span><span></p>
Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже