Форматирование с помощью функций-членов класса ios
Перегрузка операторов извлечения
Для того, чтобы перегрузить оператор извлечения — экстрактор, используется тот же самый общий подход, что и для перегрузки операторов вставки — инсертеров. Например, следующий экстрактор осуществляет ввод трехмерных координат. Обратим внимание, что он также осуществляет подсказку пользователю.
// получение трехмерных координат – экстрактор
istream& operator>>(istream &stream, point &obj)
{
cout << "Enter x y z values, separating each with a space: ";
stream >> obj.x >> obj.y >> obj.z;
return stream;
}
Первым параметром должна быть ссылка на объект типа istream. Вторым параметром служит ссылка на объект, принимающий ввод. Поскольку это именно ссылка, то второй аргумент может быть модифицирован при вводе информации. Экстрактор должен возвращать ссылку на объект типа istream.
Общая форма экстрактора имеет вид:
istream& operator>>(istream &поток, класс &объект)
{
// код экстрактора
return stream;
}
Следующая программа демонстрирует экстрактор для объекта класса point:
int main()
{
point a(1, 2, 3);
cout << a;
cin >> a; cout << a;
return 0;
}
Подобно функциям вставки, функции извлечения не могут быть членами класса, с элементами которого они оперируют. Как показано в примере, они могут быть друзьями или просто независимыми функциями. За исключением того, что экстрактор должен возвращать ссылку на объект типа istream, внутри экстрактора можно делать все, что угодно. Однако для лучшей структуризации программы и ее ясности лучше всего ограничить эти действия только операциями ввода.
7.4 Форматирование ввода/вывода
Как известно, с помощью функции printf() можно управлять форматированием информации, выводимой на экран. Например, можно указать ширину полей, левое или правое выравнивание и т.д. Все те же типы форматирования можно осуществить и в рамках ввода/вывода языка C++. Имеется два способа форматирования вывода. Первый из них использует функции-члены класса ios. Второй использует специальный тип функции, называемой манипулятор. Начнем рассмотрение форматирования с использования функций-членов класса ios.
В заголовочном файле ios.h определены следующие переменные перечисляемого типа:
Определенные таким способом значения переменных перечисляемого типа используются для установки или сброса флагов, управляющих форматированием информации потоков.
Когда установлен флаг skipws, то следующие в начале символы-разделители (пробелы, символы табуляции и новой строки) при выполнении ввода отбрасываются. Когда флаг skipws сброшен, символы-разделители сохраняются.
При установке флага left осуществляется вывод с левым выравниванием. Соответственно при установке флага right вывод осуществляется с правым выравниванием. По умолчанию вывод осуществляется с выравниванием по правому краю.
При установке флага internal поле, отведенное для вывода, заполняется пробелами до знака или буквы. Далее будет показано, как указать ширину поля.
Также по умолчанию численные значения выводятся в десятичной форме. Однако можно переопределить такой способ. Например, установка флага oct задает вывод в восьмеричной форме. Установка флага hex задает вывод в шестнадцатеричной форме. Для возвращения к десятичному выводу следует установить флаг dec.
Установка флага showbase позволяет при выводе указывать числовую базу (десятичную, восьмеричную, шестнадцатеричную).
Установка флага showpoint приводит к выводу десятичной запятой и нулей справа для всех чисел с плавающей запятой вне зависимости, нужно это или нет.
По умолчанию при выводе научных данных, т.е. при использовании экспоненциальной формы записи чисел с плавающей запятой, используется 'е' в нижнем регистре. Также при выводе шестнадцатеричных величин символ 'x' выводится в нижнем регистре. При установке флага uppercase эти символы будут выводиться в верхнем регистре.
Установка флага showpos приводит к тому, что перед положительными числами будет выводиться знак «плюс».
При установке флага scientific числа с плавающей запятой выводятся с использованием научной нотации. При установке флага fixed числа с плавающей запятой выводятся в обычной нотации. По умолчанию, когда установлен флаг fixed, для десятичных цифр выводятся шесть позиций. Когда ни один из флагов не установлен, компилятор выбирает подходящий метод сам.
При установке флага unitbuf при каждой операции вставки данные немедленно заносятся в поток.
При установке флага stdio после каждого вывода данные заносятся в потоки stdout и stderr.
При установке флага boolalpha можно вводить и выводить данные булевого типа.
Флаги хранятся в формате длинных целых. В проекте стандарта ANSI для языка C++ этот тип обозначается как fmtflags, но в MS Visual C++ 7.0 флаги имеют тип long.
Для установки флагов используется функция setf(), чей формат имеет следующий вид:
long setf(long flags);
Эта функция возвращает предыдущее значение флага и присваивает ему значение параметра flags. При этом все остальные флаги остаются неизменными. Например, для того чтобы установить флаг showbase, можно использовать следующую инструкцию:
stream.setf(ios::showbase);
Здесь stream является каким-либо потоком, на который надо оказать воздействие. Например, следующая программа устанавливает флаги showpos и scientific для cout:
#include <iostream.h>
int main()
{
cout.setf(ios::showpos);
cout.setf(ios::scientific);
cout << 123 << " " << 123.23 << " ";
return 0;
}
Данная программа выводит на экран следующие данные:
+123 +1.232300е+02
С помощью операции побитового ИЛИ можно установить одновременно столько флагов, сколько надо. Например, можно изменить программу таким образом, что будет осуществляться только один вызов функции setf() при установке обоих флагов scientific и showpos:
cout.setf(ios::scientific || ios::showpos);
Для отключения флагов надо использовать функцию unsetf(). Она имеет следующий прототип:
long unsetf(long flags);
Функция возвращает значение состояния флага и сбрасывает флаги, определяемые параметром flags.
Иногда бывает полезным знать текущую установку флагов. Можно получить текущие значения флагов, используя функцию flags(), которая имеет следующий прототип:
long flags() const;
Эта функция возвращает текущие значения флагов, ассоциированных с тем потоком, членом которого она является. Используя следующую форму функции flags(), можно установить значения флагов на указанные в параметрах flags() и возвратить предыдущие значения флагов:
long flags(long new_flags);
Для того чтобы познакомиться с работой функций flags() и unsetf(), рассмотрим следующую программу. Она включает в себя функцию, называемую showflags(), которая выводит значения флагов.
#include <iostream.h>
void showflags(long f)
{
for(long i=0x8000; i; i = i >> 1)
if(i & f) cout << “1“;
else cout << "0";
cout << "\n";
}
int main()
{
long f = cout.flags();
showflags(f);
cout.setf(ios::showpos | ios::scientific);
f = cout.flags();
showflags(f);
cout.unsetf(ios::scientific);
f = cout.flags();
showflags(f);
return 0;
}
Исполнение данной программы выдаст следующие данные:
Кроме флага форматирования также можно установить ширину поля потока, символ для заполнения и число цифр после десятичной запятой. Для этого используются следующие функции:
int width(int len);
char fill(char ch);
int precision(int num);
Функция width() возвращает текущую ширину поля потока и устанавливает новое значение, определяемое параметром len. По умолчанию ширина поля изменяется в зависимости от числа символов, необходимых для записи данных. Функция fill() возвращает текущее значение символа для заполнения, которым по умолчанию является пробел, и устанавливает текущий символ заполнения равным ch. Символ заполнения используется для заполнения пустых мест в соответствии с заданной спецификацией ширины поля. Функция precision() возвращает число цифр после десятичной запятой и устанавливает новое значение, задаваемое параметром пит. Ниже представлена программа, демонстрирующая использование этих трех функций:
#include <iostream.h>
int main()
{
cout.setf(ios::showpos | ios::scientific);
cout << 123 << " " << 123.23 << "\n";
cout.precision(2); // две цифры после запятой
cout.width(10); // поле из десяти символов
cout << 123 << " " << 123.23 << "\n";
cout.fill('#'); // заполнение символом #
cout.width(10); // поле из десяти символов
cout << 123 << " " << 123.23;
return 0;
}
Программа выдаст следующий результат:
+123 +1.232300е+02
+123 +1.23е+02
######+123 +1.23е+02
Надо помнить, что флаги одного потока независимы от флагов другого потока. Изменение флагов в одном потоке не влияет на флаги другого.
Система ввода/вывода C++ включает второй способ изменения параметров форматирования потока. Для этого используются специальные функции, называемые манипуляторами (manipulators), которые могут включаться в выражения ввода/вывода. Стандартные манипуляторы показаны в таблице 7.1.
Манипулятор
Назначение
Ввод/вывод
dec
Ввод/вывод данных в десятичной форме
ввод и вывод
endl
Вывод символа новой строки с передачей в поток всех данных из буфера
вывод
ends
Вывод нулевого символа
вывод
flush
Сброс в поток содержимого буфера
вывод
hex
Ввод/вывод данных в шестнадцатеричном виде
ввод и вывод
oct
Ввод/вывод данных в восьмеричном виде
ввод и вывод
resetiosflags(long f)
Сбрасывает флаги, указанные в f
ввод и вывод
setbase(int base)
Устанавливает базу счисления равной параметру base
вывод
setfill(int ch)
Устанавливает символ заполнения равным ch
вывод
setiosflags(long f)
Устанавливает флаги, указанные в f
ввод и вывод
setprecision(int p)
Устанавливает число цифр после запятой
вывод
setw(int w)
Устанавливает ширину поля равной w
вывод
ws
Пропускает начальный символ-разделитель
ввод
Таблица 7.1. Манипуляторы ввода/вывода C++
Для использования манипуляторов с параметрами в программу необходимо включить заголовочный файл iomanip.h.
Манипуляторы могут использоваться в составе выражений ввода/вывода. Ниже представлен пример программы, использующей манипуляторы для изменения формата вывода:
Обратим внимание, как манипуляторы появляются в последовательности операторов ввода/вывода. Когда манипуляторы не имеют аргументов, как манипулятор endl в этой программе, за ними не следуют скобки. Причина этого в том, что оператору << передается адрес манипулятора.
Следующая программа использует функцию setiosflags() для установки флагов scientific и showpos потока cout: