Помимо представления и системы счисления нужно также представить пользователю выходные данные в «аккуратном» виде. Некоторые выходные данные можно поместить, например, в таблицы, чтобы сделать их максимально читабельными.
Все это можно сделать с помощью потоков вывода. Некоторые из этих настроек важны и при преобразовании входных значений. В данном примере мы познакомимся с так называемыми манипуляторами ввода-вывода. Часть их могут показаться сложными, так что рассмотрим их подробнее.
Как это делается
В этом примере мы выведем на экран числа, используя совершенно разные настройки форматов, чтобы ознакомиться с манипуляторами ввода-вывода.
1. Сначала включим все необходимые заголовочные файлы и объявим об использовании пространства имен
std
:
#include
#include
#include
using namespace std;
2. Далее определим вспомогательную функцию, выводящую на экран целое число в разных стилях. Она принимает ширину отступа и символ-заполнитель, которым по умолчанию является
' '
:
void print_aligned_demo(int val,
size_t width,
char fill_char = ' ')
{
3. С помощью
setw
можно задать минимальное количество символов, которое нужно вывести. Например, при вводе числа 123
с шириной 6
получим " 123"
или " 123"
. Можно управлять тем, с какой стороны будут вставлены символы-заполнители, используя std::left
, std::right
и std::internal
. При выводе чисел в десятичной форме манипулятор internal
выглядит похожим на right
. Но если мы выведем значение 0x1
, например с шириной 6
и манипулятором internal
, то получим "0x 6"
. Манипулятор setfill
определяет, какой именно символ будет применен для заполнения. Мы попробуем разные стили.
cout << "================\n";
cout << setfill(fill_char);
cout << left << setw(width) << val << '\n';
cout << right << setw(width) << val << '\n';
cout << internal << setw(width) << val << '\n';
}
4. В функции
main
начинаем использовать реализованную нами функцию. Сначала выведем на экран значение 12345
с шириной 15
. Сделаем это дважды, но во второй раз применим заполнитель '_'
.
int main()
{
print_aligned_demo(123456, 15);
print_aligned_demo(123456, 15, '_');
5. Затем выведем значение
0x123abc
с такой же шириной. Однако прежде, чем это сделать, применим std::hex
и std::showbase
с целью указать объекту потока вывода cout
, что он должен выводить числа в шестнадцатеричном формате и добавлять к ним префикс "0x"
, поскольку тогда их нельзя будет интерпретировать по-другому:
cout << hex << showbase;
print_aligned_demo(0x123abc, 15);
6. Сделаем то же самое и для
oct
, что укажет cout
использовать восьмеричную систему счисления при выводе чисел. showbase
все еще активен, поэтому 0
будет добавлен к каждому выводимому числу:
cout << oct;
print_aligned_demo(0123456, 15);
7. В случае использования
hex
и uppercase
мы увидим, что символ 'x'
в конструкции "0x"
будет выведен в верхнем регистре. Сочетание 'abc'
в конструкции '0x123abc'
также будет в верхнем регистре:
cout << "A hex number with upper case letters: "
<< hex << uppercase << 0x123abc << '\n';
8. Если мы хотим снова вывести число
100
в десятичной системе счисления, то нужно запомнить, что мы переключили поток в режим hex
. С помощью dec
вернем его в обычное состояние:
cout << "A number: " << 100 << '\n';
cout << dec;
cout << "Oops. now in decimal again: " << 100 << '\n';
9. Мы также можем сконфигурировать формат вывода булевых значений. По умолчанию значение
true
выводится как 1
, а false
— как 0
. С помощью boolalpha
можно задать ему текстовое представление:
cout << "true/false values: "
<< true << ", " << false << '\n';
cout << boolalpha
<< "true/false values: "
<< true << ", " << false << '\n';
10. Взглянем на переменные с плавающей точкой типов
float
и double
. Если нужно вывести число наподобие 12.3
, то увидим 12.3
. При наличии такого числа, как 12.0
, поток вывода отбросит десятичную точку. Это можно изменить, использовав showpoint
. С его помощью десятичная точка будет отображаться всегда:
cout << "doubles: "
<< 12.3 << ", "
<< 12.0 << ", "
<< showpoint << 12.0 << '\n';
11. Представление чисел с плавающей точкой может иметь модификаторы
scientific
или fixed
. Первый означает следующее: число нормализовано к такому виду, что видна только первая цифра после десятичной точки, а затем выводится экспонента, на которую нужно умножить число, чтобы получить его реальный размер. Например, значение 300.0
будет выглядеть как "3.0E2"
, поскольку 300
равно 3.0 * 10^2
. Модификтор fixed
позволяет вернуться к представлению с десятичной точкой:
cout << "scientific double: " << scientific
<< 123000000000.123 << '\n';
cout << "fixed double: " << fixed
<< 123000000000.123 << '\n';
12. Помимо нотации мы можем решить, какую точность будут иметь числа с плавающей точкой. Создадим очень маленькое значение и выведем его, указав точность десять знаков после запятой, а затем повторим вывод, но укажем точность один знак:
cout << "Very precise double: "
<< setprecision(10) << 0.0000000001 << '\n';
cout << "Less precise double: "
<< setprecision(1) << 0.0000000001 << '\n';
}
13. Компиляция и запуск программы дают следующий длинный результат. Первые четыре блока выходных данных получены от вспомогательной функции
print
с помощью модификаторов setw
и left/right/internal
. Затем мы работали с регистрами представлений системы счисления, представлениями булевых чисел, а также форматированием чисел с плавающей точкой. Вам стоит поработать со всеми этими возможностями, чтобы получше ознакомиться с ними.
$ ./formatting
================
123456
123456
123456
================
123456__________
__________123456
__________123456
================
0x123abc
0x123abc
0x 123abc
================
0123456
0123456