Использование стандартных потоков вывода-вывода

 

Поверхностное ознакомление с вводом и выводом в «родном» C++, которое было рассмотрено ранее, является достаточным для его использования в примерах в процессе дальнейшего изучения языка. А при программировании для Windows оно и вовсе не понадобится. Ввод и вывод C++ вращается вокруг понятия потоков данных; данные можно вставлять в выходной поток и принимать из входного. Стандартный выходной поток ISO / ANSI C++ в командную строку на экране называется cout. Дополняющий его входной поток с клавиатуры известен под именем cin.

Естественно, в языке С существует форматирование вывода на экран. Форматирование осуществляется при помощи манипуляторов и функций выходного потока. К сожалению потоки являются объектно-ориентированным расширением языка С, то есть они используются в формате языка С++, а не С, поэтому формат использования функций ввода-вывода необходимо просто запомнить, а понимание придёт после изучения языка С++.

Манипулятор модифицирует способ управления выводом данных в поток (или вводом из потока). Манипуляторы определены в заголовочном файле <iomanip>, поэтому необходимо добавить директиву #include с этим файлом.

При выводе данные могут быть отформатированы с помощью функций-членов класса или манипуляторов. Их перечень приводится в табл. 3.

Манипуляторы, начинающиеся с "no" (noshowpos и т. п.), имеют обратное действие по отношению к манипуляторам с такими же именами, но без приставки "no".

// Пример программы для оформления вывода

#include <iostream>

#include <iomanip> // Подключение заголовочного файла

// для манипуляторов

using namespace std;

int main(void)

{

double d1=1234.7894, d2=0.00456;

float f1=-12.567,f2=0.67842;

int i1=1294, i2=-5971;

cout.precision(4); // Задет количество цифр после точки

// (=4) для вещественных чисел

cout<<showpos; // К положительным числам добавляет знак +

/* Строка вывода, состоящая из № строки вывода, и 6 значений переменных, занимающих по 12 позиций вывода. В конце переход на новую строку */

cout<<"1\t"<<setw(12)<<d1<<setw(12)<<d2<<setw(12)<<f1<<setw(12)<<f2<<setw(12)<<i1<<setw(12)<<i2<<endl;

cout.unsetf(ios::showpos); // Отмена добавления +

cout<<"2\t"<<setw(12)<<d1<<setw(12)<<d2<<setw(12)<<f1<<setw(12)<<f2<<setw(12)<<i1<<setw(12)<<i2<<endl;

cout<<showpoint; // Установка обязательной точки

// для вещественных чисела

cout<<"3\t"<<setw(12)<<d1<<setw(12)<<d2<<setw(12)<<f1<<setw(12)<<f2<<setw(12)<<i1<<setw(12)<<i2<<endl;

cout.unsetf(ios::showpoint); //Отмена обязательной точки

cout<<scientific; // Вывод вещественных чисел

// в научном виде

cout<<"4\t"<<setw(12)<<d1<<setw(12)<<d2<<setw(12)<<f1<<setw(12)<<f2<<setw(12)<<i1<<setw(12)<<i2<<endl;

cout<<fixed; // Вывод вещественных чисел

// в обямном формате

Таблица 3

Манипуляторы и функции стандартного ввода/вывода в C++

Манипуляторы Функции-члены класса Описание
showpos setf(ios::showpos) Выдает знак плюс у выводимых положительных чисел
noshowpos unsetf(ios::showpos) Убирает знак + у выводимых положительных чисел
showpoint setf(ios::showpoint) В обязательном порядке ставит символ точки при выводе дробного числа, даже если дробная часть =0
noshowpoint unsetf(ios::showpoint) Убирает символ десятичной точки
boolalpha setf(ios::boolalpha) Переводит тип bool в символьный, то есть вместо 0 и 1 печатает значения true и false
noboolalpha unsetf(ios::boolalpha) Возврат значений 0 и 1 для типа bool
internal setf(ios::internal, ios::adjustfield) Добавляет символы-заполнители к определенным внутренним позициям выходного потока. Если такие позиции не определены, поток не изменяется
left setf(ios::left, ios :: adjustfield) Добавляет символы-заполнители с конца числа (сдвигая число влево)
right setf(ios::right,ios:: adjustfield) Добавляет символы-заполнители с начала числа (сдвигая число вправо)
dec setf(ios::dec, ios:: basefield) Числа будут выводиться в формате 10-тиричных чисел. Только для положительных целых
hex setf(ios::hex,ios:: basefield) Числа будут выводиться в формате 16-тиричных чисел. Только для положительных целых
oct setf(ios::oct,ios:: basefield) Числа будут выводиться в формате 8-миричных чисел. Только для положительных целых
fixed setf(ios::fixed, ios :: floatfield) Переводит выход с плавающей точкой в выход с фиксированной точкой
scientific setf(ios::scientific, ios:: floatfield) Выдает числа с плавающей точкой в виде, используемом в научных целях: например, число 23450000 будет записано как: 23.45е6
fill(с) setfill(char_type c) Задает символ заполнения при выводе данных. Значения по умолчанию – пробел.
precision(n) setprecision(int n) Задает точность вывода данных (количество цифр после точки) Значения по умолчанию – 6
setw(int n) width(n) Задает ширину поля для выводимых данных (количество символов) Для каждого выводимого числа надо добавлять заново. Значения по умолчанию – 0
endl

Вставляет символ новой строки ('\n') в выходную последовательность символов и сбрасывает буфер ввода

ends

Вставляет символ '\0' в выходную последовательность символов


cout<<"5\t"<<setw(12)<<d1<<setw(12)<<d2<<setw(12)<<f1<<setw(12)<<f2<<setw(12)<<i1<<setw(12)<<i2<<endl;

/* Если установлено количество позиций для числа при помощи setw , то можно использовать c ледующие возможности*/

cout.setf(ios::left); // Расположить число по левому краю

cout<<"6\t"<<setw(12)<<d1<<setw(12)<<d2<<setw(12)<<f1<<setw(12)<<f2<<setw(12)<<i1<<setw(12)<<i2<<endl;

cout<<right; // Расположить число по правому краю

cout<<"7\t"<<setw(12)<<d1<<setw(12)<<d2<<setw(12)<<f1<<setw(12)<<f2<<setw (12)<<i1<<setw(12)<<i2<<endl;

cout<<internal<<showpos; // Расположить число по краям, если можно

cout<<"8\t"<<setw(12)<<d1<<setw(12)<<d2<<setw(12)<<f1<<setw(12)<<f2<<setw (12)<<i1<<setw (12)<<i2<<endl;

cout.setf(ios::left); // Для удобства восприятия вернемся к левому краю

// Вывод в 8-миричном формате, только для положительных целых чисел

cout<<oct;

cout<<"9\t"<<setw(12)<<d1<<setw(12)<<d2<<setw(12)<<f1<<setw(12)<<f2<<setw (12)<<i1<<setw(12)<<i2<<endl;

// Вывод в 16-тиричном формате, только для положительных целых чисел

cout.setf(ios::hex,ios:: basefield);

cout<<"10\t"<<setw(12)<<d1<<setw(12)<<d2<<setw(12)<<f1<<setw(12)<<f2<<setw (12)<<i1<<setw (12)<<i2<<endl;

// Вывод в 10-тиричном формате, только для положительных целых чисел

cout.setf(ios::dec, ios:: basefield);

cout<<"11\t"<<setw(12)<<d1<<setw(12)<<d2<<setw(12)<<f1<<setw(12)<<f2<<setw (12)<<i1<<setw(12)<<i2<<endl;

cout.fill('#'); // Использование заполнителя (#), а не пробела

cout<<internal; // Для нагладности заполнение по центру

cout<<"12\t"<<setw(12)<<d1<<setw(12)<<d2<<setw(12)<<f1<<setw(12)<<f2<<setw (12)<<i1<<setw(12)<<i2<<endl;

return 1;

}

Формат использования манипулятора:

cout<<манипулятор (из 1 колонки таблицы)

Формат использования функций-методов:

cout.setf(параметр); для установки значений

cout.unsetf(параметр);для снятия значения

Результат работы программы представлен на рис. 19.

 

Рис. 19. Результат работы программы, демонстрирующей работу с форматным выводом

 

На всех 12 строках выведены одни и те же значения 6 переменных. Таким образом, можно сделать вывод, что при использовании форматированного вывода можно добиться большого разнообразия внешнего вида данных в консольном приложении.

При выводе на экран символьной строки, заключенной в двойные кавычки, можно включать в нее специальные символы, называемые управляющими последовательностями (escape sequences). Они так называются потому, что позволяют поместить в строку символы, которые не могут быть представлены иным образом, их не много \ t — символ табуляции (7 или 5 пробелов), \ n — окончание строки или переход на следующую строку, \ a — звуковой сигнал .

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

cin.getline(имя переменной-строки,длины строки,'символ разделитель');

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

Приведём пример программы для иллюстрации возникающих проблем.

// Пример программы для демонстрации проблем при вводе

#include <iostream>

#include <iomanip>

using namespace std;

int main(void)

{

char str[20]; // Строка длинной 20 символов

int i;

float f;

cout<<"Enter float f, int i, and stroky str\n";

cin>>f>>i>>str; // считывание данных, где в строке

// нет пробелов

cout<<setw (12)<<i<<setw (12)<<f<<"\t"<<str<<endl;

cin>>f>>i;

cin.getline(str,20,'\n'); // использование методов

// для ввода пробелов

cout<<setw (12)<<i<<setw (12)<<f<<"\t"<<str<<endl;

return 0;

}

Результат работы программы может быть следующим (рис. 20). Возможно два варианта развития событий. В первом случае возможна ситуация, когда в первом операторе ввода строка вводится с пробелом, то второй оператор ввода данных не работает, так как символьные значения, введённые после пробела обрабатываются как неправильно введённые числовые значения из второго оператора ввода.

 

Рис. 20. Результат работы программы в первом варианте

 

Второй случай возникает, если первый раз строка была введена без пробелов и все числовые данные вводятся правильно, но ввод строки при помощи метода cin.getline(str,20,'\n'); не работает, строка получается пустой (рис. 21). Это происходит из-за того, что используются различные механизмы ввода, после ввода числовых значений в буфере ввода остаётся символ конца строки (код нажатия клавиши Enter) его и использует функция getline(); при получении данных, этот символ является символом конца ввода строки, и функция прекращает работу. Для исправления этой ошибки необходимо внести следующие изменения в код программы.

 

Рис. 21. Результат работы программы во втором варианте

 

Перед строкой cin.getline(str,20,'\n'); необходимо добавить строку cin.ignore(1,'\n'); – это предложение программе игнорировать 1 символ конца строки.

Итоговый результат работы программы выглядит следующим образом (рис. 22).

 

Рис. 22. Итоговый вариант работы программы

 

Можно обратить внимание на проблему, с которой сталкиваются все пользователи программ, использующие национальные алфавиты, расположенные во второй половине кодировочных таблиц. (В первой половине кодировочной таблице обычно располагаются специальные символы и латинский алфавит.) В этом пособии рассматриваются консольные приложения, которые являются программами, выполняемыми под управлением командной строки (наследие DOS). Такие программы использую кодировочную таблицу DOS, MS VS 2008 – это приложение под Windows и кодировочная таблица здесь другая. Текст программы набирается в редакторе MS VS 2008 и кодируется под Windows, консольное приложение выполняется под DOS. При попытке вывести на экран текстовую строку, содержащую русские буквы происходит вывод других символов, так как первая часть кодировочных таблиц совпадает, вторая – отличается. При вводе с клавиатуры значений символьной переменной на экране отображаются русские буквы, потому что эта переменная содержит правильные коды, но при сравнении с любым константным значением коды одинаковых символов не совпадают. Решение этой проблемы будет рассмотрено в главе 6 (русификатор).

Частично эту проблему можно решить, используя команду подключения кодировочной таблицы внутри функции main():

setlocale(NULL,"Russian");

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

Контрольные вопросы и задания

1) Каким образом в программном коде оформляются комментарии?

2) Приведите типичную структуру программы, написанной на языке С.

3) Каким образом будет передаваться управление внутри программы, если в функции main() вызывать другие функции?

4) В какой библиотеке определены операторы cout и endl? Как будут выглядеть их полные имена?

5) Что такое директивы препроцессора, для каких целей они используется?

6) Дайте определения оператора в языке С. Какие виды операторов Вам известны? Приведите пример оператора.

7) Приведите примеры операторов ввода-вывода. Какой заголовочный файл необходимо подключать для использования этих операторов?

8) Что такое составной оператор? Каким образом его можно реализовать в программе на языке С?

9) Приведите пример использования стандартных потоков вывода-вывода.

10) Приведите пример использования потока вывода с возможностью форматирования.

ГЛАВА 5