C++17 STL Стандартная библиотека шаблонов — страница 67 из 119

Помимо представления и системы счисления нужно также представить пользователю выходные данные в «аккуратном» виде. Некоторые выходные данные можно поместить, например, в таблицы, чтобы сделать их максимально читабельными.

Все это можно сделать с помощью потоков вывода. Некоторые из этих настроек важны и при преобразовании входных значений. В данном примере мы познакомимся с так называемыми манипуляторами ввода-вывода. Часть их могут показаться сложными, так что рассмотрим их подробнее.


Как это делается

В этом примере мы выведем на экран числа, используя совершенно разные настройки форматов, чтобы ознакомиться с манипуляторами ввода-вывода.


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