Ввод-вывод данных в С/С++
1. Форматный ввод-вывод данных в С
2. Ввод-вывод данных в С++
Лабораторная работа 1. Программирование ввода-вывода с использованием функций PRINTF(), SCANF()
1. Функции семейства ...printf-это универсальные функции вывода в С
Способ преобразования, форматирования и печати каждого их аргумента полностью управляется форматной строкой.
Форматная строка-это символьная строка, содержащая два типа объектов:
- обычные символы, которые просто копируются в выходной поток;
- спецификации формата ( начинаются с символа %).
Каждая спецификация задается в форме
[флаги][ширина][.точность][F|N|h|l]вид_преобразования
Символы, определяющие вид преобразования ...printf
Символ
|
Тип аргумента
|
Формат вывода
|
d
|
целый
|
целое десятичное со знаком
|
i
|
целый
|
целое десятичное со знаком
|
o
|
целый
|
целое восьмеричное без знака
|
u
|
целый
|
целое десятичное без знака
|
x
|
целый
|
целое шестнадцатеричное без знака с символами a, b, c, d, e, f
|
X
|
целый
|
тот же, что и для x, но с символами A, B, C, D, E, F
|
f
|
с плавающей точкой
|
значение в форме [ -] ...dd.dddddd
|
e
|
с плавающей точкой
|
значение в форме [ -] d.dddddd e [ +/-] ddd
|
g
|
с плавающей точкой
|
значение в форме e или f в зависимости от его величины и точности; незначащие нули и десятичная точка печатаются только при необходимости
|
E
|
с плавающей точкой
|
тот же, что и для e, но экспонента обозначается символом E
|
G
|
с плавающей точкой
|
тот же, что и для g, но с символом E для экспоненты
|
c
|
символ
|
отдельный символ
|
s
|
указатель на строку
|
печатаются символы строки, пока не встретится символ '/0'
|
%
|
( нет )
|
печатается символ %
|
n
|
указатель на int
|
по указанному адресу запоминается количество напечатанных к этому моменту символов
|
p
|
указатель
|
аргумент печатается как указатель
|
Спецификатор ширины указывает минимальную ширину поля вывода
Значение
|
Воздействие на формат вывода
|
n
|
печатать по крайней мере n символов, если выводимое значение содержит менее n символов, то поле дополняется пробелами;
|
0n
|
печатать по крайней мере n символов, дополняя поле слева нулями, если выводимых символов менее n;
|
*
|
значение ширины поля является аргументом функции ...printf, предшествующим в списке аргументов выводимому значению
|
Спецификатор точности определяет количество печатаемых символов
Значение Воздействие на формат вывода
- не указано - устанавливается значение точности по умолчанию :
- 1-для d, i, o, u, x, X,
- 6-для e, E, f
- все значащие цифры - для g, G,
- все символы до первого '\0' - для s
- .0 - для e, E, f-подавляется печать десятичной точки
- .n
- для d, i, o, u, x, X-печатается по крайней мере n символов; если фактическое значение содержит менее n символов, то оно слева дополняется до n символов нулями; если в фактическом значении более n символов, то они печатаются полностью без какого-либо отсечения
- для e, E, f-печатается n цифр после десятичной точки; перед отбрасыванием лишних разрядов производится округление
- для g, G-печатается не более n значащих цифр
- для s-печатается не более n символов
Флаги для ...printf
- выравнивание результата по левому краю поля и заполнение справа оставшейся части поля пробелами; -если не указан, то выравнивание по правому краю поля и заполнение слева оставшейся части поля пробелами или нулями
+ результат преобразований d, i, e, g, E, Gвсегда печатается с его знаком: плюс ( +) или минус ( -)
пробел для положительных чисел вместо знака выводится символ пробела
# предписывает вывод результата преобразования в "альтернативной" форме:
- для c, s, d, i, u-не оказывает действия
- для o-вывод 0перед ненулевым результатом
- для x, X-вывод соответственно 0xили 0Xперед результатом
- для e, E, f-всегда выводить десятичную точку
- для g, G-как и для e и E и без подавления незначащих нулей
Замечание. Флаги могут указываться в любых комбинациях и в любом порядке. Флаг ( +) имеет больший приоритет, чем флаг "пробел", если они присутствуют одновременно.
Функции семейства ...scanf-универсальные функции ввода в языке Си.
Единицей вводимой и обрабатываемой ими информации является входное поле.
Порядок следования полей, их содержимое и способ обработки полностью определяются форматной строкой.
Форматная строка -это символьная строка, содержащая три типа объектов:
- "прозрачные" символы (пробел, символы табуляции и новой строки);
- "непрозрачные" символы (любые символы ASCII за исключением "прозрачных" и символа процента %);
- спецификации формата (начинаются с символа %).
"Прозрачные" символы являются разделителями полей в потоке данных. Встречая "прозрачный" символ в форматной строке, функция ...scanf будет читать и отбрасывать все подряд идущие "прозрачные" символы во входном потоке вплоть до первого "непрозрачного".
Встречая "непрозрачный" символ в форматной строке, функция ...scanf должна прочитать и отбросить такой же символ во входном потоке, а при обнаружении их несовпадения -прекратить свою работу.
Спецификации формата имеют следующую форму :
%[*][ширина][F|N][h|l|L]вид_преобразования
* признак подавления присваивания (пропуск поля );
ширина максимальная ширина поля, но его сканирование может закончиться и раньше, если встретится "прозрачный" или не преобразуемый по данному формату символ.
F|N аргумент-адрес есть соответственно far-указатель (F) или near-указатель (N), применяются для "перекрытия" значения, принятого по умолчанию;
h|l аргумент-адрес есть указатель на short(h) или long(l) соответственно, используются для "перекрытия" умолчания;
L аргумент-адрес есть указатель на long double.
Символы, определяющие вид преобразования для ...scanf
Символы
|
Вводимое значение
|
Тип аргумента-адреса
|
d
|
десятичное целое
|
int *arg
|
o
|
восьмеричное целое
|
тот же
|
i
|
10-е, 8-е или 16-е целое
|
тот же
|
u
|
10-ецелое беззнака
|
unsigned int*arg
|
x, X
|
16-е целое
|
int*arg
|
f, e, g, E, G
|
с плавающей точкой
|
float*arg
|
s
|
символьная строка
|
char arg[ ]
|
c
|
символ
|
char*arg или char arg[W+1], если задана ширина поля W
|
%
|
символ процента
|
вводится символ процента
|
n
|
( нет )
|
int*arg, значением arg станет число успешно введенных до %n символов
|
p
|
16-е
|
YYYY:XXXX far*arg или near*arg, используется или XXXX форма, принятая по умолчанию в зависимости от модели памяти.
|
Для ввода строки можно задавать в квадратных скобках поисковый образ, например, %[0-9A-F+-]s.
В этом случае функция ...scanf для текущего аргумента будет вводить поле, содержащее только перечисленные символы. Здесь квадратные скобки обязательны.
Наоборот, спецификация %[^abcd]s предписывает вводить любые символы, кроме перечисленных.
Функции ...scanf прекращают свое выполнение в следующих ситуациях:
1. Очередной символ во входном поле не совпадает с очередным "непрозрачным” символом в форматной строке.
2. Прочитан признак конца файла EOF.
3. Исчерпана форматная строка.
Все функции ...scanf возвращают количество присвоенных значений ( без учета пропускаемых полей ) или EOF при чтении конца файла (конца строки для sscanf).
Замечание. Если в обращении к функции ...scanf количество аргументов-адресов больше, чем число спецификаций преобразования, заданных в форматной строке, то лишние аргументы игнорируются.
Но если их меньше, чем спецификаций - это ошибка с непредсказуемыми последствиями.
Пример. Ввод даты в форме 23-feb-89 с выделением ее составных частей - числа, месяца и года.
Предшествующие "прозрачные" символы и внутренние символы тире отбрасываются.
scanf(" %d-%s-%d",&day,&month,&year);
2. Ввод-вывод данных в С++
Ввод и вывод в С++ организован с помощью так называемых поточных классов, содержащих данные и методы работы с файлами по вводу/выводу.
Поточные классы происходят от общего предка — класса ios и потому наследуют его функциональность. Чтобы начать писать программу с использованием ввода/вывода на языке С++, следует обязательно выполнить в программе:
#include <iostream>
Класс iostream является потомком классов istream и ostream. Эти же классы являются родителями классов ifstream и оfstream.
Классы istream, ostream, лежащие в основе поточных классов, содержат стандартные объекты-экземпляры классов с именами cout (экземпляр класса для стандартного ввода), cin (экземпляр класса для стандартного вывода) и сerr (экземпляр класса для стандартного вывода сообщений об ошибках).
При запуске любой программы на языке С++ эти стандартные потоки определены (открыты) и по умолчанию назначены на стандартное вводное устройство — клавиатуру (cin), на стандартное выводное устройство — экран (cout и cerr). Причем все эти устройства синхронно связаны с соответствующими указателями stdin, stdout, stderr. Так что работа со стандартным вводом/выводом сводится к тому, что вместо задаваемых пользователем имен экземпляров соответствующих классов задаются имена стандартных экземпляров классов: cin, cout. Открывать ничего не нужно, надо только использовать операции <<, >> и операции форматирования. Если мы пишем имена переменных, из которых выводятся или в которые вводятся данные, то по умолчанию для ввода/вывода используются определенные форматы.
Например, запишем: cout << i;
В этом случае значение i выведется на экран в формате, определенном по умолчанию для типа i и в минимальном поле.
Запишем: cin >> i >> j >> s;
где i, j, s описаны соответственно как int, float, char. В записи мы не видим форматов, но при вводе значений этих переменных с клавиатуры (после ввода каждого значения надо нажимать клавишу <Enter>) их форматы будут учтены.
Стандартный вывод cout
Объект cout направляет данные в буфер-поток, связанный с объектом stdout, объявленным в файле stdio.h. По умолчанию стандартные потоки С и С++ синхронизированы.
При выводе данные могут быть отформатированы с помощью функций-членов класса или манипуляторов. Перечень их приведен в табл. 1.
Манипуляторы, начинающиеся с приставки "no" (noshowpos и т. п.) имеют обратное действие по отношению к манипуляторам с такими же именами, но без "no". В графе "Описание" у таких манипуляторов поставлены прочерки.
Таблица 1. Манипуляторы и функции стандартного ввода/вывода в С++
Манипуляторы
|
Функции-члены класса
|
Описание
|
showpos
|
setf(ios::showpos)
|
Выдает знак плюс у выводимых положительных чисел
|
noshowpos
|
unsetf(ios::showpos)
|
—
|
showbase
|
setf(ios::showbase)
|
Выдает базу системы счисления в выводимом числе в виде префикса
|
noshowbase
|
unsetf(ios::showbase)
|
—
|
uppercase
|
setf(ios::uppercase)
|
Заменяет символы нижнего регистра на символы верхнего регистра в выходном потоке
|
nouppercase
|
unsetf(ios::uppercase)
|
—
|
showpoint
|
setf(ios::showpoint)
|
Создает символ десятичной точки в сгенерированном потоке с плавающей точкой (в выводимом числе)
|
noshowpoint
|
unsetf(ios::showpoint)
|
—
|
boolalpha
|
setf(ios::boolalpha)
|
Переводит булевый тип в символьный
|
noboolalpha
|
unsetf(ios::boolalpha)
|
—
|
unitbuf
|
setf(ios::unitbuf)
|
Сбрасывает буфер вывода после каждой операции вывода
|
nounitbuf
|
unsetf(ios::unitbuf)
|
—
|
internal
|
setf(ios::internal,ios::adjustfield)
|
Добавляет символы-заполнители к определенным внутренним позициям выходного потока (речь идет о выводе числа в виде потока символов). Если такие позиции не определены, поток не изменяется
|
left
|
setf(ios::left, ios::adjustfield)
|
Добавляет символы-заполнители с конца числа (сдвигая число влево)
|
right
|
setf(ios::right, ios::adjustfield)
|
Добавляет символы-заполнители с начала числа (сдвигая число вправо)
|
dec
|
setf(ios::dec, ios::basefield)
|
Переводит базу вводимых или выводимых целых чисел в десятичную (введенные после этого манипулятора данные будут выводиться как десятичные)
|
hex
|
setf(ios::hex, ios::basefield)
|
Переводит базу вводимых или выводимых целых чисел в шестнадцатеричную (введенные после этого манипулятора данные будут выводиться как шестнадцатеричные)
|
oct
|
setf(ios::oct, ios::basefield)
|
Переводит базу вводимых или выводимых целых чисел в восьмеричную (введенные после этого манипулятора данные будут выводиться как восьмеричные)
|
fixed
|
setf(ios::fixed, ios::floatfield)
|
Переводит выход с плавающей точкой в выход с фиксированной точкой
|
scientific
|
setf(ios::scientific, ios::floatfield)
|
Выдает числа с плавающей точкой в виде, используемом в научных целях: например, число 23450000 будет записано как: 23.45e6
|
|
setbase(int base)
|
Преобразует ввод целых чисел в тип base, где параметр base может быть одним из чисел 8, 10 или 16
|
fill(c)
|
setfill(char_type c)
|
Задает символ заполнения при выводе данных
|
precision(n)
|
setprecision(int n)
|
Задает точность вывода данных (количество цифр после точки)
|
setw(int n)
|
width(n)
|
Задает ширину поля для выводимых данных (количество символов)
|
endl
|
|
Вставляет символ новой строки ('\n') в выходную последовательность символов и сбрасывает буфер ввода
|
ends
|
|
Вставляет символ '\0' в выходную последовательность символов
|
flush
|
flush()
|
Сбрасывает буфер вывода
|
ws
|
|
Задает пропуск пробелов при вводе
|
Значения по умолчанию:
- precision() — 6;
- width() — 0;
- fill() — пробел.
Пример 2.
Пример программы с применением объекта cout.
#include <vcl.h>
#include <iostream>
#include <iomanip> // включение манипуляторов
#include <conio.h>
void main()
{
using namespace std;
int i;
float f;
cout << "Enter i and f >" << endl;
// чтение целого числа и числа с плавающей точкой с устройства stdin
cin >> i >> f;
// вывод целого и переход на новую строку
cout << i << endl;
// вывод числа с плавающей точкой и переход на новую строку
cout << f << endl;
// вывод в шестнадцатеричной системе
cout << hex << i << endl;
// вывод в восьмеричной и десятичной системах
cout << oct << i << dec << i << endl;
// вывод i с указанием его знака
cout << showpos << i << endl;
// вывод i в шестнадцатеричной системе
cout << setbase(16) << i << endl;
/* вывод i в десятичной системе и дополнение справа символом @ до ширины в 20 символов (заполнение начинается от правой границы к левой). */
cout << setfill('@') << setw(20) << left << dec << i;
cout << endl;
// вывод того же результата в том же формате, но с использованием функций вместо манипуляторов
cout.fill('@');
cout.width(20);
cout.setf(ios::left, ios::adjustfield);
cout.setf(ios::dec, ios::basefield);
cout << i << endl;
// вывод f в научной нотации с точностью — 10 цифр
cout << scientific << setprecision(10) << f << endl;
// изменение точности до 6 цифр
cout.precision(6);
// вывод f и возврат к нотации с фиксированной точкой
cout << f << fixed << endl;
getch();
}
Стандартный ввод cin
Объект (экземпляр класса) cin управляет вводом из буфера ввода, связанного с объектом stdin, объявленным в файле stdio.h.
cin>>переменная;
По умолчанию стандартные потоки в языках С и С++ синхронизированы. При вводе используется часть тех функций и манипуляторов, которые определены для cout. Это такие манипуляторы, как dec, hex, oct, ws и др.
Пример 3.
Пример программы с использованием объекта cin.
// cin example #1
#include <vcl.h>
#include <fstream>
#include <iostream>
#include <conio.h>
void main()
{
using namespace std;
int i;
float f;
char c;
// ввод целого числа, числа с плавающей точкой и символа с stdin
cout << "Enter i,f,c and then input the string >" << endl;
cin >> i >> f >> c;
// вывод i, f и c на stdout
cout << i << endl << f << endl << c << endl;
// cin example #2
char p[50];
// приказ на удаление из ввода всех пробелов
cin >> ws >> p;
cout << p << endl;
// чтение символов с stdin, пока не будет нажата клавиша <Enter>
// или не будут прочтены 49 символов
cin.seekg(0);
cin.getline(p,50);
// вывод результата на stdout
cout << p << endl;
getch();
}
Ввод/вывод с использованием разных классов
Итак, мы определили, что поточные классы — это поставщики инструментов
для работы с файлами. В поточных классах хранятся:
- структуры, обеспечивающие открытие/закрытие файлов;
- функции (методы) открытия/закрытия файлов;
- другие функции и данные, обеспечивающие, как мы увидим далее, собственно ввод/вывод.
Пространства имен
Многие серьезные приложения состоят из нескольких программных файлов (с исходным текстом программ), которые создаются и обслуживаются отдельными группами программистов. И только после этого все файлы собираются в общий проект. Но как быть с тем фактом, что в таких файлах могут быть одинаково объявлены разные переменные? В С++ это неудобство разрешается с помощью так называемых пространств имен, которые вводятся в каждый текстовый программный файл проекта с помощью директивы:
Namespace <имя_пространства_имен (идентификатор)>
{ в эти скобки заключается весь программный текст }
Когда идет сборка общего проекта, то в итоговом тексте пишут директиву:
using namespace:: <идентификатор_пространства_имен>;
Это обеспечивает в итоговом проекте доступ к переменным файла с данным пространством имен. При использовании поточных классов языка С++ в основной программе требуется писать директиву:
using namespace::std;
В противном случае программа не пройдет компиляцию.
Пример 1.
Использование директив пространства имен.
#include <vcl.h>
#include <iostream>
#include <conio.h>
namespace F
{ float x = 9; }
namespace G
{
using namespace F; /* здесь само пространство G использует пространство F, и в нем же объявляется еще одно пространство: INNER_G */
float y = 2.0;
namespace INNER_G
{
float z = 10.01;
}
} // G
int main()
{
using namespace G; /* эта директива позволяет пользоваться всем, объявленным в пространстве G */
using namespace G::INNER_G; /* эта директива позволяет пользоваться всем, объявленным только в INNER_G */
float x = 19.1; /*локальное объявление переопределяет предыдущее */
std::cout << "x = " << x << std::endl;
std::cout << "y = " << y << std::endl; // y берется из пространства F
std::cout << "z = " << z << std::endl; /* z берется из пространства INNER_G */
getch();
return 0;
}
В результате на экране появится:
x = 19.1
y = 2
z = 10.01
std::cout — это стандартный вывод. Здесь показано, что объект cout принадлежит пространству имен std.
|