Опубликован: 26.06.2003 | Уровень: для всех | Доступ: платный
Лекция 17:

Ввод-вывод

< Лекция 16 || Лекция 17: 123 || Лекция 18 >

Манипуляторы и форматирование ввода-вывода

Часто бывает необходимо вывести строку или число в определенном формате. Для этого используются так называемые манипуляторы.

Манипуляторы – это объекты особых типов, которые управляют тем, как ostream или istream обрабатывают последующие аргументы. Некоторые манипуляторы могут также выводить или вводить специальные символы.

С одним манипулятором мы уже сталкивались, это endl. Он вызывает вывод символа новой строки. Другие манипуляторы позволяют задавать формат вывода чисел:

endl при выводе перейти на новую строку;
ends вывести нулевой байт (признак конца строки символов);
flush немедленно вывести и опустошить все промежуточные буферы;
dec выводить числа в десятичной системе (действует по умолчанию);
oct выводить числа в восьмеричной системе;
hex выводить числа в шестнадцатиричной системе счисления;
setw (int n) установить ширину поля вывода в n символов ( n – целое число);
setfill(int n) установить символ-заполнитель; этим символом выводимое значение будет дополняться до необходимой ширины;
setprecision(int n) установить количество цифр после запятой при выводе вещественных чисел;
setbase(int n) установить систему счисления для вывода чисел; n может принимать значения 0, 2, 8, 10, 16, причем 0 означает систему счисления по умолчанию, т.е. 10.

Использовать манипуляторы просто – их надо вывести в выходной поток. Предположим, мы хотим вывести одно и то же число в разных системах счисления:

int  x  =  53;
cout << "Десятичный вид:        " << dec  
     <<  x  <<  endl
     << "Восьмеричный вид:      " << oct  
     <<  x  <<  endl
     << "Шестнадцатеричный вид: " << hex  
     <<  x  <<  endl

Аналогично используются манипуляторы с параметрами. Вывод числа с разным количеством цифр после запятой:

double x;
// вывести число в поле общей шириной 
// 6 символов (3 цифры до запятой, 
// десятичная точка и 2 цифры после запятой)
cout  <<  setw(6)  <<  setprecision(2) << fixed <<  x  <<  endl;

Те же манипуляторы (за исключением endl и ends ) могут использоваться и при вводе. В этом случае они описывают представление вводимых чисел. Кроме того, имеется манипулятор, работающий только при вводе, это ws. Данный манипулятор переключает вводимый поток в такой режим, при котором все пробелы (включая табуляцию, переводы строки, переводы каретки и переводы страницы) будут вводиться. По умолчанию эти символы воспринимаются как разделители между атрибутами ввода.

int x;
// ввести шестнадцатеричное число
cin  >>  hex  >>  x;

Строковые потоки

Специальным случаем потоков являются строковые потоки, представленные классом strstream. Отличие этих потоков состоит в том, что все операции происходят в памяти. Фактически такие потоки формируют форматированную строку символов, заканчивающуюся нулевым байтом. Строковые потоки применяются, прежде всего, для того, чтобы облегчить форматирование данных в памяти.

Например, в приведенном в предыдущей главе классе Exception для исключительной ситуации можно добавить сообщение. Если мы хотим составить сообщение из нескольких частей, то может возникнуть необходимость форматирования этого сообщения:

// произошла ошибка
strstream  ss;
ss  << "Ошибка ввода-вывода, регистр: " 
    <<  oct  <<  reg1;
ss  << "Системная ошибка номер: " << dec  
    <<  errno  <<  ends;
String  msg(ss.str());
ss.rdbuf()->freeze(0);
Exception ex(Exception::INTERNAL_ERROR, msg);
throw  ex;

Сначала создается объект типа strstream с именем ss. Затем в созданный строковый поток выводятся сформатированные нужным образом данные. Отметим, что в конце мы вывели манипулятор   ends, который добавил необходимый для символьной строки байтов нулевой байт. Метод str() класса strstream предоставляет доступ к сформатированной строке (тип его возвращаемого значения – char* ). Следующая строка освобождает память, занимаемую строковым потоком (подробнее об этом рассказано ниже). Последние две строки создают объект типа Exception с типом ошибки INTERNAL_ERROR и сформированным сообщением и вызывают исключительную ситуацию.

Важное свойство класса strstream состоит в том, что он автоматически выделяет нужное количество памяти для хранения строк. В следующем примере функция split_numbers выделяет числа из строки, состоящей из нескольких чисел, разделенных пробелом, и печатает их по одному на строке.

#include  <strstream.h>
void
split_numbers(const  char* s)
{
    strstream  iostr;
    iostr  <<  s  <<  ends;
    int  x;
    while  (iostr  >>  x) 
    cout  <<  x<<  endl;
}
int
main()
{
    split_numbers("123  34  56  932");
    return 1;
}

Замечание. В среде Visual C++ файл заголовков называется strstream.h.

Как видно из этого примера, независимо от того, какова на самом деле длина входной строки, объект iostr автоматически выделяет память, и при выходе из функции split_numbers, когда объект уничтожается, память будет освобождена.

Однако из данного правила есть одно исключение. Если программа обращается непосредственно к хранимой в объекте строке с помощью метода str (), то объект перестает контролировать эту память, а это означает, что при уничтожении объекта память не будет освобождена. Для того чтобы память все-таки была освобождена, необходимо вызвать метод rdbuf()->freeze(0) (см. предыдущий пример).

< Лекция 16 || Лекция 17: 123 || Лекция 18 >
Андрей Одегов
Андрей Одегов
Язык программирования C++
Елена Шумова
Елена Шумова

Здравствуйте! Я у Вас прошла курс Язык программировая Си++.

Заказала сертификат. Хочу изменить способ оплаты. Как это сделать?

Иван Иванов
Иван Иванов
Россия, Екатеринбург
Владимир Алемасов
Владимир Алемасов
Россия