3.5. Лексический анализ строки, содержащей число в экспоненциальной форме

3.5. Лексический анализ строки, содержащей число в экспоненциальной форме

Проблема

Имеется строка, содержащая число в экспоненциальной форме, и требуется сохранить значение числа в переменной типа double.

Решение

Наиболее простым способом анализа числа в экспоненциальной форме является использование встроенного в библиотеку C++ класса stringstream, объявленного в <sstream>, как показано в примере 3.7.

Пример 3.7. Лексический анализ числа в экспоненциальной форме

#include <iostream>

#include <sstream>

#include <string>

using namespace std;

double sciToDub(const strings str) {

 stringstream ss(str);

 double d = 0;

 ss >> d;

 if (ss.fail()) {

  string s = "Невозможно отформатировать ";

  s += str;

  s += " как число!";

  throw (s);

 }

 return (d);

}

int main() {

 try {

  cout << sciToDub("1.234e5") << endl;

  cout << sciToDub("6.02e-2") << endl;

  cout << sciToDub("asdf") << endl;

 } catch (string& e) {

  cerr << "Ошибка: " << e << endl;

 }

}

Далее показан вывод этого кода.

123400

0.0602

Ошибка: невозможно отформатировать asd как число!

Обсуждение

Класс stringstream — это string, который ведет себя как поток (что неудивительно). Он объявлен в <sstring>. Если требуется выполнить анализ string, содержащей число в экспоненциальной форме (см. также рецепт 3.2), то с этой работой прекрасно справится stringstream. Стандартные классы потоков уже «знают», как анализировать числа, так что не тратьте без острой необходимости время на повторную реализацию этой логики.

В примере 3.7 я написал простую функцию sciToDub, принимающую параметр типа string и возвращающую содержащийся в ней double, если он допустим. В sciToDub я использую stringstream следующим образом.

stringstream ss(str); // Конструирование из строки типа string

double d = 0;

ss >> d;

if (ss.fail()) {

 string s = "Невозможно отформатировать ";

 s += str;

 s += " как число!";

 throw (s);

}

return (d);

Наиболее важной частью здесь является то, что все, что требуется сделать, — это использовать для чтения из строкового потока в double оператор сдвига вправо (>>), как это делается при чтении из cin.

Ну, это не совсем все, что требуется сделать. Если в stringstream записано значение, которое не может быть записано в переменную в правой части оператора >>, то для потока будет выставлен бит fail. Этот бит можно проверить с помощью функции-члена fail (на самом деле это функция-член basic_ios, который является родительским классом для stringstream). Кроме того, переменная справа от оператора >> в случае ошибки значения не меняет.

Однако с целью обобщения можно избежать написания отдельных версий sciToDub для типов int, float, double и чего-либо еще, что может потребоваться преобразовать, если написать шаблон функции. Рассмотрим такую новую версию.

template<typename T>

T strToNum(const string& str) {

 stringstream ss(str);

 T tmp;

 ss >> tmp;

 if (ss.fail()) {

  string s = "Невозможно отформатировать ";

  s += str;

  s += " как число!";

  throw (s);

 }

 return (tmp);

}

Теперь, чтобы преобразовать string в числовой тип, можно сделать так.

double d = strToNum<double>("7.0");

float f = strToNum<float>("7.0");

int i = strToNum<int>("7.0");

Также параметром шаблона можно сделать тип символов, но это очень просто сделать, так что я оставляю это в качестве вашего упражнения.

Смотри также

Рецепт 3.2.