basic_string<Unicode> a_unicode_string;
Стандартный класс
string
, который мы используем, является просто классом
basic_string
, конкретизированным обычным типом
char
.
typedef basic_string<char> string; // строка — это basic_string<char>
Мы не будем описывать символы или строки кода Unicode, но при необходимости вы можете работать с ними точно так же, как и с обычными символами и строками (к ним применяются точно такие же конструкции языка, класс
string
, потоки класса
iostream
и регулярные выражения). Если вам нужны символы кода Unicode, то лучше всего попросить совета у опытных пользователей; для того чтобы ваша программа стала полезной, вы должны не только выполнять правила языка, но и некоторые системные соглашения.
В контексте обработки текста важно помнить, что практически все можно представить в виде строки символов. Например, на этой странице число
12.333
представлено в виде строки, состоящей из шести символов и окруженной пробелами.
Если вы считываете это число, то должны сначала превратить эти символы в число с плавающей точкой и лишь потом применять к нему арифметические операции. Это приводит к необходимости конвертирования чисел в объекты класса
string
и объектов класса
string
в числа. В разделе 11.4 мы видели, как превратить целое число в объект класса
string
, используя класс ostringstream. Этот прием можно обобщить для любого типа, имеющего оператор
<<
.
template<class T> string to_string(const T& t)
{
ostringstream os;
os << t;
return os.str();
}
Рассмотрим пример.
string s1 = to_string(12.333);
string s2 = to_string(1+5*6–99/7);
Значение строки
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<class T> T from_string(const string& s)
{
istringstream is(s);
T t;
if (!(is >> t)) throw bad_from_string();
return t;
}
Рассмотрим пример.
double d = from_string<double>("12.333");
void do_something(const string& s)
try
{
int i = from_string<int>(s);
// ...
}
catch (bad_from_string e) {
error ("Неправильная строка ввода",s);
}
Дополнительная сложность функции
from_string()
по сравнению с функцией
to_string()
объясняется тем, что класс
string
может представлять значения многих типов. Это значит, что каждый раз мы должны указывать, какой тип значений хотим извлечь из объекта класса
string
. Кроме того, это значит, что класс
string
, который мы изучаем, может не хранить значение типа, который мы ожидаем. Рассмотрим пример.
int d = from_string<int>("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<T>(s)) // для всех s
и
t==from_string<T>(to_string(t)) // для всех t
Здесь слово “разумный” означает, что тип
T
должен иметь конструктор по умолчанию, оператор
>>
и соответствующий оператор
<<
.