Основной вопрос, возникающий при жестком кодировании строк в коде Unicode, связан с выбором способа ввода строки в редакторе исходных текстов. В C++ предусмотрен тип расширенного набора символов wchar_t
, который может хранить строки в коде Unicode. Точное представление wchar_t
зависит от реализации, однако часто используется формат UTF-32. Класс wstring
определяется в <string>
как последовательность символов типа wchar_t
, подобно тому как класс string
представляет собой последовательность символов типа char
. (Строго говоря, тип wstring
определяется, конечно, с помощью typedef
как basic_string<wchar_t>
.)
Самый простой способ ввода символов в коде Unicode — это использование префикса L
перед строковым литералом, как показано в примере 13.1.
wstring ws1 = L'Infinity, u2210'; // Использовать сам код
wstring ws2 = L'Euro: €'; // или просто ввести символ
Теперь можно записать эти строки с расширенным набором символов в поток с расширенным набором символов.
wcout << ws1 << endl; // wcout - версия cout для расширенного набора символов
Их можно записывать также в файлы:
wofstream out('tmp\unicode.txt');
out << ws2 << endl;
При работе с различными кодировками наибольшую ловкость приходится проявлять не для ввода правильных символов в ваши исходные файлы, а при определении типа символьных данных, получаемых из базы данных, по запросу HTTP, из пользовательского ввода и т.д., что выходит за рамки стандарта C++. Стандарт C++ не устанавливает никаких специальных требований, кроме того, что операционная система может использовать для исходных файлов любую кодировку, если она поддерживает, по крайней мере, 96 символов, используемых в языке С++. Для символов, не попадающих в этот набор, называемый uXXXX
или UXXXXXXXX
, где X
— шестнадцатеричная цифра.
13.2. Запись и чтение чисел
Требуется записать число в поток в форматированном виде в соответствии с местными соглашениями.
Закрепите (imbue) текущую локализацию за потоком, в который вы собираетесь писать данные, и запишите в него числа, как это сделано в примере 13.2, или можете установить глобальную локализацию и затем создать поток. Последний подход рассматривается в обсуждении.
#include <iostream>
#include <locale>
#include <string>
using namespace std;
// На заднем плане существует глобальная локализация, установленная средой
// этапа выполнения. По умолчанию это локализация 'С'. Вы можете ее
// заменить локализацией locale::global(const locale&).
int main() {
locale loc(''); // Создать копию пользовательской локализации
cout << 'Locale name = ' << loc.name() << endl;
cout.imbue(loc); // Уведомить cout о необходимости применения
// пользовательской локализации при форматировании
cout << 'pi in locale ' << cout.getloc().name() << ' is << 3.14 << endl;
}
Пример 13.2 показывает, как можно использовать пользовательскую локализацию для форматирования числа с плавающей точкой. Это делается в два этапа: сначала создается экземпляр класса locale
, который затем закрепляется за потоком с помощью функции imbue
.
Сначала в примере 13.2 создается loc
, который является копией пользовательской локализации. Это необходимо делать, используя конструктор locale
, принимающий пустую строку (а не конструктор по умолчанию).
locale loc('');
Отличие небольшое, но важное, и я вскоре вернусь к нему. При создании здесь объекта locale
создается копия «пользовательской локализации», которая зависит от реализации. Это значит, что, если машина сконфигурирована на применение американского варианта английского языка, функция locale::name()
может возвращать такие строковые имена локализации, как «en_US
», «English_United States.1252
», «english-american
» и т.д. Реальная строка определяется реализацией, а по стандарту C++ достаточно иметь только одну локализацию — «C»-локализацию.
Для сравнения отметим, что конструктор по умолчанию класса locale
возвращает копию текущей locale
(возможно, реализованный как статическая переменная где-то в библиотеке этапа выполнения; детали его реализации зависят от используемой платформы). По умолчанию это будет локализация С, и вы можете заменить ее локализацией locale::global(locale& loc)
. Когда потоки создаются, они используют глобальную локализацию, существующую на момент их создания; это означает, что cin
, cout
, cerr
, wcin
, wcout
и wcerr
используют локализацию С, поэтому приходится явным образом ее менять, если требуется, чтобы форматирование подчинялось соглашениям, принятым в определенной местности.
Имена локализаций не стандартизованы. Однако обычно они имеют следующий формат.
<язык>_<страна>.<кодовая_страница>
Язык задается полным названием, например «Spanish
», или двухбуквенным кодом, например «sp
»; страна задается своим названием, например «Colombia
», или двухбуквенным кодом страны, например «СО
», а кодовая страница задается своим обозначением, например 1252
. Обязательно должен быть указан только язык. Поэкспериментируйте, явно задавая локализации в различных системах, чтобы почувствовать характер отличий имен при применении разных компиляторов. Если вы используете неверное имя локализации, будет выброшено исключение runtime_error
. Пример 13.3 показывает, как можно явно задавать имена локализаций.
#include <iostream>
#include <fstream>