18c0693f

Состояние формата


Каждый объект класса из библиотеки iostream поддерживает состояние формата, которое управляет выполнением операций форматирования, например основание системы счисления для целых значений или точность для значений с плавающей точкой. Для модификации состояния формата объекта в распоряжении программиста имеется предопределенный набор манипуляторов.1 Манипулятор применяется к потоковому объекту так же, как к данным. Однако вместо чтения или записи данных манипулятор модифицирует внутреннее состояние потока. Например, по умолчанию объект типа bool, имеющий значение true (а также литеральная константа true), выводится как целая ‘1’:

#include <iostream.h>

int main()

{

    bool illustrate = true;

    cout << "объект illustrate типа bool установлен в true: "

         << illustrate << '\n';

}

Чтобы поток cout выводил переменную illustrate в виде слова true, мы применяем манипулятор boolalpha:

#include <iostream.h>

int main()

{

    bool illustrate = true;

    cout << "объект illustrate типа bool установлен в true: ";

    // изменяет состояние cout так, что булевские значения

    // печатаются в виде строк true и false

    cout << boolalpha;

    cout << illustrate << '\n';

}

Поскольку манипулятор возвращает потоковый объект, к которому он применялся, то допустимо прицеплять его к выводимым данным и другим манипуляторам. Вот как можно перемежать данные и манипуляторы в нашей программе:

#include <iostream.h>

int main()

{

    bool illustrate = true;

    cout << "объект illustrate типа bool: "

         << illustrate

         << "\nс использованием boolalpha: "

         << boolalpha << illustrate << '\n';

    // ...

}

Вывод данных и манипуляторов вперемежку может сбить пользователя с толку. Применение манипулятора изменяет не только представление следующего за ним объекта, но и внутреннее состояние потока. В нашем примере все значения типа bool в оставшейся части программы также будут выводиться в виде строк.


Чтобы отменить сделанную модификацию потока cout, необходимо использовать манипулятор noboolalpha:

cout << boolalpha   // устанавливает внутреннее состояние cout

     << illustrate

     << noboolalpha  // сбрасывает внутреннее состояние cout

Как мы покажем, для многих манипуляторов имеются парные.

По умолчанию значения арифметических типов читаются и записываются в десятичной системе счисления. Программист может изменить ее на восьмеричную или шестнадцатеричную, а затем вернуться к десятичной (это распространяется только на целые типы, но не на типы с плавающей точкой), пользуясь манипуляторами hex, oct и dec:

#include <iostream>

int main()

{

           int ival = 16;

           double dval = 16.0;

          

           cout << "ival: " << ival

          << " установлен oct: " << oct << ival << "\n";

           cout << "dval: " << dval

          << " установлен hex: " << hex << dval << "\n";

           cout << "ival: " << ival

          << " установлен dec: " << dec << ival << "\n";

}

Эта программа печатает следующее:

ival: 16 установлен oct: 20

dval: 16 установлен hex: 16

ival: 10 установлен dec: 16

Но, глядя на значение, мы не можем понять, в какой системе счисления оно записано. Например, 20 – это действительно 20 или восьмеричное представление 16? Манипулятор showbase выводит основание системы счисления вместе со значением с помощью следующих соглашений:

  • 0x в начале обозначает шестнадцатеричную систему (если мы хотим, чтобы вместо строчной буквы 'x' печаталась заглавная, то можем применить манипулятор uppercase, а для отмены – манипулятор nouppercase);


  • 0 в начале обозначает восьмеричную систему;


  • отсутствие того и другого обозначает десятичную систему.


  • Вот та же программа, но и с использованием showbase:



    #include <iostream>

    int main()

    {

               int ival = 16;

               double dval = 16.0;

         cout << showbase;

              

               cout << "ival: " << ival

              << " установлен oct: " << oct << ival << "\n";

               cout << "dval: " << dval

              << " установлен hex: " << hex << dval << "\n";

               cout << "ival: " << ival

              << " установлен dec: " << dec << ival << "\n";

         cout << noshowbase;

    }

    Результат:

    ival: 16 установлен oct: 020

    dval: 16 установлен hex: 16

    ival: 0x10 установлен dec: 16

    Манипулятор noshowbase восстанавливает состояние cout, при котором основание системы счисления не выводится.

    По умолчанию значения с плавающей точкой выводятся с точностью 6. Эту величину можно модифицировать с помощью функции-члена precision(int) или манипулятора setprecision(); для использования последнего необходимо включить заголовочный файл iomanip. precision() возвращает текущее значение точности. Например:

    #include <iostream>

    #include <iomanip>

    #include <math.h>

    int main()

    {

               cout << "Точность: "

              << cout.precision() << endl

              << sqrt(2.0) << endl;

         cout.precision(12);

               cout << "\nТочность: "

                    << cout.precision() << endl

              << sqrt(2.0) << endl;

               cout << "\nТочность: "  << setprecision(3)

                    << cout.precision() << endl

                    << sqrt(2.0) << endl;

               return 0;

    }

    После компиляции и запуска программа печатает следующее:

    Точность: 6

    1.41421

    Точность: 12

    1.41421356237

    Точность: 3

    1.41



    Манипуляторы, принимающие аргумент, такие, как setprecision() и setw(), требуют включения заголовочного файла iomanip:

    #include <iomanip>

    Кроме описанных аспектов,  setprecision() имеет еще два: на целые значения он не оказывает никакого влияния; значения с плавающей точкой округляются, а не обрезаются. Таким образом, при точности 4 значение 3.14159 печатается как 3.142, а при точности 3 – как 3.14.

    По умолчанию десятичная точка не печатается, если дробная часть значения равна 0. Например:

    cout << 10.00

    выводит

    10

    Чтобы точка выводилась, воспользуйтесь манипулятором showpoint:

    cout << showpoint

         << 10.0

         << noshowpoint << '\n';

    Манипулятор noshowpoint восстанавливает поведение по умолчанию.

    По умолчанию значения с плавающей точкой выводятся в нотации с фиксированной точкой. Для перехода на научную нотацию используется идентификатор scientific, а для возврата к прежней нотации – модификатор fixed:

    cout << "научная: " << scientific

         << 10.0

         << "с фиксированной точкой: " << fixed

         << 10.0 << '\n';

    В результате печатается:

    научная: 1.0e+01

    с фиксированной точкой: 10

    Если бы мы захотели вместо буквы 'e' выводить 'E', то следовало бы употребить манипулятор uppercase, а для возврата к 'e' – nouppercase. (Манипулятор uppercase не приводит к переводу букв в верхний регистр при печати.)

    По умолчанию перегруженные операторы ввода пропускают пустые символы (пробелы, знаки табуляции, новой строки и возврата каретки). Если дана последовательность:

    a bc

    d

    то цикл

    char ch;

    while ( cin >> ch )

         // ...

    читает все буквы от 'a' до 'd' за четыре итерации, а пробельные разделители оператором ввода игнорируются. Манипулятор noskipws отменяет такой пропуск пробельных символов:

    char ch;

    cin >> noskipws;

    while ( cin >> ch )

         // ...

    cin >> skipws;

    Теперь цикл while будет выполняться семь раз. Чтобы восстановить поведение по умолчанию, к потоку cin применяется манипулятор skipws.



    Когда мы пишем:

    cout << "пожалуйста, введите значение: ";

    то в буфере потока cout сохраняется литеральная строка. Есть ряд условий, при которых буфер сбрасывается (т.е. опустошается), – в нашем случае в стандартный вывод: