13.2. Запись и чтение чисел
13.2. Запись и чтение чисел
Проблема
Требуется записать число в поток в форматированном виде в соответствии с местными соглашениями.
Решение
Закрепите (imbue) текущую локализацию за потоком, в который вы собираетесь писать данные, и запишите в него числа, как это сделано в примере 13.2, или можете установить глобальную локализацию и затем создать поток. Последний подход рассматривается в обсуждении.
Пример 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 показывает, как можно явно задавать имена локализаций.
Пример 13.3. Явное именование локализаций
#include <iostream>
#include <fstream>
#include <locale>
#include <string>
using namespace std;
int main() {
try {
locale loc("");
cout << "Locale name = " << loc.name() << endl;
locale locFr("french");
locale locEn("english-american");
locale locBr("portuguese-brazilian");
cout.imbue(locFr); // Уведомить cout о необходимости применения
// французского форматирования
cout << "3.14 (French) = " << 3.14 << endl;
cout << "Name = " << locFr.name() << endl;
cout.imbue(locEn); // Теперь перейти на английский (американский
// вариант)
cout << "3.14 (English) = " << 3.14 << endl;
cout << "Name = " << locEn.name() << endl;
cout.imbue(locBr); // Уведомить cout о необходимости применения
// бразильского форматирования
cout << "3.14 (Brazil) = " << 3.14 << endl;
cout << "Name = " << locBr.name() << endl;
} catch (runtime_error& e) {
// Если используется неверное имя локализации, выбрасывается исключение
// runtime_error.
cerr << "Error: " << e.what() << endl;
}
}
Результат выполнения этой программы в системе Windows при использовании Visual C++ 7.1 выглядит следующим образом.
Locale name = English_United States.1252
3.14 (French) = 3,14
Name = French_France.1252
3.14 (English) = 3.14
Name = English_United States.1252
3.14 (Brazil) = 3,14
Name = Portuguese_Brazil.1252
Отсюда видно, что моя машина локализована на американский вариант английского языка с использованием кодовой страницы 1252. Этот пример также показывает, как выводится число «пи» при использовании двух других локализаций. Обратите внимание, что во французском и бразильском вариантах применяется запятая вместо десятичной точки. Разделитель тысяч тоже другой: во французском и португальском вариантах используется пробел вместо запятой, поэтому число 1,000,000.25, представленное в американском формате, имело бы вид 1 000 000,25 в формате французской и португальской локализации.
В большинстве случаев все же не стоит создавать локализации, явно задавая их имена. Чтобы использовать локализации для печати чисел, дат, денежных и каких-либо других значений, просто достаточно инстанцировать локализацию, используя пустую строку и закрепляя ее за потоком.
Правила применения локализаций могут показаться немного путанными, поэтому я кратко изложу основные моменты.
• Используемая по умолчанию глобальная локализация является локализацией «С», потому что стандартом гарантируется существование в любой реализации только этой локализации.
• Все стандартные потоки создаются с применением глобальной локализации при запуске программы, и этой локализацией является локализация «С».
• Копию пользовательской текущей локализации можно создать, передавая пустую строку конструктору locale, например locale("").
• Объект locale для именованной локализации можно создать, передавая строку, идентифицирующую локализацию, например locale("portuguese-brazilian"). Однако эти строки не стандартизованы.
• После получения объекта locale, представляющего стандартную пользовательскую локализацию или именованную локализацию, можно установить глобальную локализацию с помощью функции locale::global. Все создаваемые после этого потоки будут использовать глобальную локализацию.
• Для потока локализацию можно задать явно при помощи функции-члена imbue.
При написании программного обеспечения, учитывающего местные особенности, применяйте локализованное форматирование только к данным, которые пользователь видит. То есть если вам требуется отобразить число в формате, привычном для пользователя, инстанцируйте локализацию и закрепите ее за потоком, чтобы число отображалось правильно. Однако, если вы записываете данные в файл или в какую-то другую промежуточную сериализованную память, используйте локализацию С, обеспечивая переносимость. Если ваша программа явным образом изменяет глобальную локализацию, вам необходимо явно закрепить ее за потоками, использующими локализацию С. Вы это можете сделать двумя способами: создавая локализацию с именем «С» или вызывая функцию locale::classic().
ofstream out("data.dat");
out.imbue(locale::classic());
out << pi << endl; // Записать, используя локализацию С
Считываются числа аналогично. Например, для чтения числа с использованием французской локализации и записи его с использованием локализации С выполните следующее.
double d;
cin.imbue(locale("french"));
cin >> d;
cout << "In English: " << d;
Если вы выполните эту программу и введете 300,00, она распечатает 300.
Чтобы поток подчинялся местным соглашениям по выводу чисел, явно закрепите за потоком объект locale целевой локализации с помощью функции imbue. Если требуется во всех созданных потоках использовать конкретную локализацию, сделайте ее глобальной. Значения денежных значений обрабатываются немного по-другому; см. рецепт 13.4, где показано, как можно записывать и считывать денежные значения.
Смотри также
Рецепт 13.4.
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКЧитайте также
10.5.2. Чтение и запись из процесса
10.5.2. Чтение и запись из процесса Хотя system() отображает результат работы команды на устройство стандартного вывода и позволяет дочерним программам читать стандартный ввод, это не всегда идеально. Часто процесс желает читать вывод другого процесса либо отправлять текст на
11.2.4. Чтение, запись и перемещение
11.2.4. Чтение, запись и перемещение Хотя есть несколько способов читать и писать файлы, мы обсудим здесь только простейшие из них[42]. Чтение и запись почти идентичны, поэтому рассмотрим их одновременно.#include <unistd.h>size_t read(int fd, void * buf, size_t length);size_t read(int fd, const void * buf, size_t length);Обе
11.2.5. Частичное чтение и запись
11.2.5. Частичное чтение и запись Хотя обе функции — и read(), и write() — принимают параметр, указывающий, сколько байт нужно прочитать или записать, ни одна из них не гарантирует, что обработает указанное количество байт, даже если не случается никаких ошибок. Простейший пример
Чтение и запись
Чтение и запись freadЧитает из открытого файла определенное количество символов.Синтаксис:string fread(int $f, int $numbytes)Читает из файла $f $numbytes символов и возвращает строку этих символов. После чтения указатель файла продвигается к следующему после прочитанного блока позициям. Если
18.3. Чтение и запись
18.3. Чтение и запись Создав маршрутизирующий сокет, процесс может отправлять ядру команды путем записи в этот сокет и считывать из него информацию от ядра. Существует 12 различных команд маршрутизации, 5 из которых могут быть запущены процессом. Они определяются в
19.2. Чтение и запись
19.2. Чтение и запись Все сообщения в сокете управления ключами должны иметь одинаковые заголовки, соответствующие листингу 19.1[1]. Сообщение может сопровождаться различными расширениями в зависимости от наличия дополнительной информации или необходимости ее
Чтение и запись данных
Чтение и запись данных Есть несколько VBA-команд для записи и извлечения данных из файла. В приведенной ниже таблице описана их работа. Пример команды Использование Пояснение Оператор Put Предназначен для записи переменных в файл Put #1, 1800, StrQuote (записывает переменную
13.3. Запись и чтение дат и времен
13.3. Запись и чтение дат и времен ПроблемаТребуется отобразить или прочитать значения дат и времен, используя местные соглашения по форматированию.РешениеИспользуйте тип time_t и tm struct из <ctime>, а также фасеты даты и времени, предусмотренные в <locale>, для записи и чтения
Чтение и запись данных
Чтение и запись данных Функции ввода/вывода верхнего уровня позволяют передавать данные различными способами.Операции чтения и записи в потоках начинаются с текущей. позиции в потоке, идентифицируемой как "file pointer"(указатель файла) для потока. Указатель файла изменяется
Чтение и запись данных
Чтение и запись данных Функции read и write, как и функции ввода/вывода верхнего уровня, начинают выполнение очередной операции с текущей позиции в файле. Текущая позиция изменяется при каждой операции чтения или записи.Функция eof может быть использована для проверки на конец
Чтение и запись файлов
Чтение и запись файлов Для доступа к файлам предназначены пять различных методов класса CFile: Read, ReadHuge, Write, WriteHuge, Flush. Методы Read и ReadHuge предназначены для чтения данных из предварительно открытого файла. В 16-разрядной операционной системе Windows функция Read может считать не