Вы находитесь на странице: 1из 9

Прототип: 

stdio.h
Описание: 

Функция printf() записывает в stdout аргументы из списка arg-list под управлением строки, на
которую указывает аргумент format.

Строка, на которую указывает format, состоит из объектов двух различных назначений. Во-
первых, это символы, которые сами должны быть выведены на экран. Во-вторых, это
спецификаторы формата, определяющие вид, в котором будут выведены аргументы из
списка arg-list. Спецификаторы формата состоят из символа процент, за которым следует код
формата. Команды форматирования приведены в таблице. Количество аргументов должно
точно соответствовать количеству спецификаторов формата, причем следовать они должны в
одинаковом порядке. Например, следующий вызов функции printf()

printf ("Hi %с %d %s", 'с', 10, "there!");

приведет к выводу «Hi с 10 there!».

Таблица: Команды форматирования дня printf()


Код Формат
%с Символ типа char
%d Десятичное число целого типа со знаком
%i Десятичное число целого типа со знаком
%е Научная нотация (е нижнего регистра)
%Е Научная нотация (Е верхнего регистра)
%f Десятичное число с плавающей точкой
Использует код %е или %f — тот из них, который короче (при использовании %g
%g
используется е нижнего регистра)
Использует код %Е или %f — тот из них, который короче (при использовании %G
%G
используется Е верхнего регистра)
%о Восьмеричное целое число без знака
%s Строка символов
%u Десятичное число целого типа без знака
%х Шестнадцатиричное целое число без знака (буквы нижнего регистра)
%Х Шестнадцатиричное целое число без знака (буквы верхнего регистра)
%р Выводит на экран значение указателя
Ассоциированный аргумент — это указатель на переменную целого типа, в которую
%n
помещено количество символов, записанных на данный момент
%% Выводит символ %

Если количество аргументов меньше, чем количество команд форматирования, то вывод не-
определен. Если же количество аргументов больше, чем команд форматирования, то лишние
аргументы отбрасываются.

Функция printf() возвращает количество действительно выведенных символов. Возврат


отрицательной величины означает ошибку.
Команды форматирования могут содержать модификаторы, означающие ширину поля, точ-
ность и флаг выравнивания влево. Переменная целого типа, помещенная между символом
процент и командой форматирования, работает как спецификатор минимальной ширины
поля, заполняя поле вывода пробелами или нулями так, чтобы обеспечить указанную
минимальную ширину. Если строка или число больше, чем этот минимум, они будут
полностью выведены. По умолчанию заполнение производится пробелами. При выводе
числовых переменных, если надо использовать заполнение нулями, помещается нуль перед
спецификатором минимальной ширины поля. Например, %05d будет дополнять числа,
состоящие из менее чем 5 цифр, нулями до пяти цифр.

Результат использования модификатора точности зависит от типа модифицируемой команды


форматирования. Чтобы использовать модификатор точности, надо поместить десятичную
точку и точность вслед за ней после количества выводимых десятичных знаков. Например,
%10.4f означает вывод числа шириной минимум 10 символов с четырьмя знаками после
точки. Однако при использовании совместно со спецификаторами g или G модификатор
точности задает максимальное количество отображаемых значащих цифр.

Когда модификатор точности применяется к целым числам, он указывает минимальное коли-


чество отображаемых цифр. (При необходимости отображаются предшествующие нули.)

Когда модификатор точности применяется к строкам, число после десятичной точки


указывает максимальную длину поля. Например, %5.7s выводит строку длиной не менее
пяти и не более семи символов. Если строка длиннее, чем максимальная ширина поля, то
последние символы будут урезаны.

По умолчанию вывод производится с выравниванием вправо. Это значит, что если ширина
поля больше, чем выводимые данные, то данные будут размещены на правом краю поля.
Можно задать режим выравнивания влево, вставив знак минус сразу после знака процент.
Например, %-10.2f прижмет влево в десятизнаковом поле число с плавающей точкой с двумя
знаками после запятой.

Имеется два спецификатора формата, позволяющих printf() отображать целые числа типа
short и long. Эти спецификаторы могут применяться совместно со спецификаторами типа d, i,
о, u, х и X. Спецификатор l «говорит» printf() о том, что далее следуют данные типа long.
Например, %ld означает, что нужно вывести long int. Спецификатор h указывает printf(), что
нужно отобразить short int. Следовательно, %hu указывает, что данные имеют тип short
unsigned int.

Хоть это и не оговорено в стандарте (да это и не нужно), модификатор l может также
предшествовать спецификаторам типа с плавающей точкой е, Е, f, g и G, указывая, что далее
следует переменная double. L используется для указания long double.

Спецификатор n помещает количество выведенных до сих пор символов в переменную


целого типа, на которую указывает аргумент, соответствующий спецификатору. Например,
следующий фрагмент кода выводит число 15 после строки «this is a test».

int i;
printf ("this is a test %n", &i);
printf("%d", i);

Символ # имеет особое значение, когда используется с некоторыми спецификаторами


формата функции printf(). Если поставить # перед спецификаторами a, g, G, f, е или Е, то
десятичная точка будет ставиться даже в отсутствие цифр после запятой. Если поставить #
перед спецификатором формата х, то шестнадцатиричное число будет выведено с префиксом
0х. Использование # со спецификатором о приводит к выводу префикса 0. С другими
спецификаторами формата символ # использоваться не может.

Спецификаторы минимальной ширины поля и точности могут быть представлены для printf()
не только в виде констант, но и с помощью аргументов. Это делается с помощью символа *,
который служит указателем места. При считывании форматной строки функция printf()
проводит соответствие между символами * и аргументами в том порядке, в каком они
встречаются.

Пример: 
Данная программа отображает на экране то, что указано в
комментариях:
#include <stdio.h>
int main(void)
{
/* выводит выровненное по левому краю "this is a test" в 20-
символьном поле
*/
printf("%-20s", "this is a test");
/* выводит вещественное значение с 3 цифрами после запятой в 10-
символьном поле. В результате работы увидим "      12.235"
*/
printf("%10.3f", 12.234 657);
return 0;
}

Стандартный ввод с консоли, как правило, осуществляется с помощью scanf(). Она читает
все стандартные типы данных и автоматически преобразует числа к правильному
внутреннему формату. Чем-то она похожа на свою противоположность printf(). Стандартный
вид scanf() следующий:

int scanf(const char * форматная_строка,..);

форматная_строка определяет способ чтения значений в переменные, на которые указывает


список аргументов.

Форматная строка состоит из трех типов символов:

 Спецификаторы формата
 Специальные символы
 Стандартные символы

Функция scanf() возвращает число введенных полей. Она возвращает EOF,.если


обнаруживается преждевременный конец файла.

Приложение, написанное на любом языке программирования, должно взаимодействовать


с окружающим миром. Иначе пользы от него не будет. Как правило, такое взаимодействие
осуществляется посредством ввода-вывода информации на монитор или в файл. Правда, есть
некоторое множество программ, которые не используют файловый или консольный ввод-
вывод: это программы, осуществляющие низкоуровневое взаимодействие с аппаратной
частью компьютера и периферией (ядро ОС, драйверы и пр.), но это уже экзотика.

В стандартном C++ существует два основных пути ввода-вывода информации: с помощью


потоков, реализованных в STL (Standard Template Library) и посредством традиционной
системы ввода-вывода, унаследованной от C. Если копнуть немного глубже, то окажется, что
и потоки, и традиционная система ввода-вывода для осуществления необходимых действий
используют вызовы операционной системы. И это правильно.

 Традиционный ввод-вывод
o Пример использования stdio

 Ввод-вывод с помощью потоков STL


o Пример использования потоков STL

 Взаимодействие потокового и традиционного ввода-вывода


 Заключение

Дальнейшее изложение не претендует на полноту, но описывает основные принципы


использования библиотек. Подробности использования можно посмотреть в многочисленной
литературе по C++ и STL, в MSDN и пр.

Традиционный ввод-вывод
Для использования традиционного ввода-вывода в духе C, в программу необходимо
включить заголовочный файл <cstdio>. (Разумеется, компилятор должен иметь доступ
к соответствующей объектной библиотеке для правильной сборки исполняемого файла.)

Библиотека stdio предоставляет необходимый набор функций для ввода и вывода


информации как в текстовом, так и в двоичном представлении. Следует отметить, что
в отличие от классической C-библиотеки, в современных библиотеках имеются более
безопасные аналоги «классических» функций. Как правило, они имеют такое же имя,
к которому добавлен суффикс _s. Рекомендуется использовать именно эти, безопасные
функции.

Самая Первая Программа с использованием библиотеки stdio выглядит так:

#include <cstdio>

int main()
{
printf("Hello, world!\n");
}

При запуске консольного приложения неявно открываются три потока: stdin — для ввода
с клавиатуры, stdout — для буферизованного вывода на монитор и stderr — для
небуферизованного вывода на монитор сообщений об ошибках. Эти три символа определены
посредством <cstdio>.

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

Самая Первая Программа с использование файлового вывода из библиотеки stdio выглядит


так:

#include <cstdio>

int main()
{
fprintf(stdout, "Hello, world!\n");
}

Некоторые популярные функции из stdio:

FILE *fopen(const char *filename, const char *mode) // открытие файла


int fclose(FILE *stream) // закрытие файла

int printf(const char *format, ...) // фоматированный консольный вывод


int fprintf(FILE *stream, const char *formatб, ...) // форматированный ввод
из файла
int sprintf(char *s, const char *format, ...) // форматированный вывод в
буфер (строку)

int scanf(const char *format, ...) // фоматированный консольный ввод


int fscanf(FILE *stream, const char *format, ...) // фоматированный ввод
int sscanf (const char *s, const char *format, ...) // фоматированный ввод
из буфера (строки)

int fgetc(FILE *stream) // читает символ из файла


char *fgets(char *s, int n, FILE *stream) // читает строку из файла
int fputc(int с, FILE *stream) // записывает символ в файл
int fputs(const char *s, FILE *stream) // записывает строку в файл
int getchar(void) // читает символ из stdin
char *gets(char *s) // читает строку из stdin
int putchar(int с) // записывает символ в stdout
int puts(const char *s) // записывает строку в stdout
int ungetc(int с, FILE *stream) // возвращает символ обратно в файл для
последующего чтения

Сущность FILE представляет собой структуру, в которой хранится вся информация для
управления потоком ввода-вывода.

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

Пример использования stdio


#include <cstdio>
#include <errno.h>

const char *filename = "testfile.txt";

int main()
{
FILE *fin, *fout;
int ecode;

// открытие файла для записи в текстовом режиме,


// запись данных и закрытие файла.
if((fout = fopen(filename, "w")) != NULL) {
for (int i = 0; i < 16; i++) {
if ((ecode = fprintf(fout, "%d\n", i*i)) <= 0) {
fprintf(stderr, "Write error in file \"%s\", code %d\n",
filename, ecode);
fclose(fout);
return 1;
}
}
fclose(fout);
}
else {
fprintf(stderr, "Output file open error \"%s\", code %d\n", filename,
errno);
return 1;
}

// открытие файла для чтения в текстовом режиме,


// чтение данных, форматированный вывод на консоль, закрытие файла.
int data;
int counter = 0;
if((fin = fopen(filename, "r")) != NULL) {
while ((ecode = fscanf(fin, "%d", &data)) != EOF) {
printf("%8d", data);
if (++counter % 4 == 0) {
putchar('\n');
}
}
if ((ecode = ferror(fin)) != 0) {
fprintf(stderr, "Read error in file \"%s\", code %d\n", filename,
ecode);
fclose(fin);
return 2;
}
fclose(fin);
}
else {
fprintf(stderr, "Input file open error \"%s\", code %d\n", filename,
errno);
return 2;
}
return 0;
}

Следует отметить, что существует еще одна библиотека, ориентированная исключительно


на консольный ввод-вывод — <conio.h>.

Ввод-вывод с помощью потоков STL


Для использования объектно-ориентированного консольного ввода-вывода с помощью
потоков (stream) STL в программу необходимо включить заголовочный файл <iostream>,
а для файлового ещё и <fstream>. (Разумеется, компилятор должен иметь доступ
к соответствующей объектной библиотеке для правильной сборки исполняемого файла.)

Самая Первая Программа с использованием потоков STL выглядит так:

#include <iostream>

using namespace std;

int main() {
cout << "Hello, world!\n";
}

При запуске консольного приложения неявно открываются четыре потока: сin — для ввода
с клавиатуры, сout — для буферизованного вывода на монитор, сerr — для
небуферизованного вывода на монитор сообщений об ошибках и clog — буферизованный
аналог cerr. Эти четыре символа определены посредством <iostream>.

Потоки cin, cout и cerr соответствуют потокам stdin, stdout и stderr соответственно.
Иерархия классов ввода-вывода STL достаточно сложна. Любители тонких ощущений могут
найти её описание в литературе. Впрочем, остальных также не минует чаша сия, но только
позже, когда потребуются знания чуть повыше того базового уровня, который описывается
здесь.

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


STL, а затем связать его с файлом. Для потока вывода используется класс ofstream, для
потока ввода — ifstream, для потока ввода-вывода — fstream. В каждом из этих классов есть
метод open(), который связывает поток с файлом. Проще говоря, открывает файл. Методу
передаются два параметра: имя файла и режим открытия файла. Второй параметр
представляет собой набор битовых флагов, определяющих режим открытия файла (чтение,
запись и пр.) и способ работы с данными (текстовый или двоичный режим). Второй параметр
опционален, т.е. имеет значение по умолчанию, соответствующее классу.

ifstream::open(const char *filename, ios::openmode mode = ios::in);


ofstream::open(const char *filename, ios::openmode mode = ios::out |
ios::trunc);
fstream::open(const char *filename, ios::openmode mode = ios::in | ios::out);

Вышеупомянутые классы имеют также конструкторы, позволяющие открыть файл сразу при
создании потока. Параметры этих конструкторов полностью совпадают с параметрами
метода open().

При ошибке открытия файла (в контексте логического выражения) поток получает значение
false.

Файл закрывается методом close(). Этот метод также вызывается при разрушении
экземпляров классов потоков.

Операции чтения и записи в поток, связанный с файлом, осуществляются либо с помощью


операторов << и >>, перегруженных для классов потоков ввода-вывода, либо с помощью
любых других методов классов потоков ввода-вывода.

Некоторые наиболее употребляемые методы:

// чтение данных:
getline() // читает строку из входного потока.
get() // читает символ из входного потока.
ignore() // пропускает указанное число элементов от текущей позиции чтения.
read() // читает указанное количество символов из входного потока и
сохраняет их в буфере (неформатированный ввод).

// запись данных:
flush() // вывод содержимого буфера в файл (при буферизованном вводе-выводе)
put() // выводит символ в поток.
write() // выводит в поток указанное количество символов из буфера
(неформатированный вывод)

Пример использования потоков STL


#include <iostream>
#include <fstream>

using namespace std;

const char *filename = "testfile2.txt";

int main() {
// создание потока, открытие файла для записи в текстовом режиме,
// запись данных и закрытие файла.
ofstream ostr;
ostr.open(filename);
if (ostr) {
for (int i = 0; i < 16; i++) {
ostr << i*i << endl;
if (ostr.bad()) {
cerr << "Unrecoverable write error" << endl;
return 1;
}
}
ostr.close();
}
else {
cerr << "Output file open error \"" << filename << "\"" << endl;
return 1;
}

// открытие файла (в конструкторе) для чтения в текстовом режиме,


// чтение данных, форматированный вывод на консоль, закрытие файла.
int data;
int counter = 0;
ifstream istr(filename);
if (istr) {
while (!(istr >> data).eof()) {
if (istr.bad()) {
cerr << "Unrecoverable read error" << endl;
return 2;
}
cout.width(8);
cout << data;
if (++counter % 4 == 0) {
cout << endl;
}
}
istr.close();
}
else {
cerr << "Input file open error \"" << filename << "\"" << endl;
return 2;
}

return 0;
}

Взаимодействие потокового и традиционного ввода-


вывода
Апологеты C++ рекомендуют использовать для ввода-вывода только потоки STL
и отказаться от использования традиционного ввода-вывода в духе C. Однако, ничто
не мешает, по крайней мере пока, использовать традиционную систему ввода-вывода. Более
того, предусмотрена специальная функция для синхронизации ввода-вывода, выполненного
посредством потоков и посредством старых функций.

#include <iostream>
bool sync_with_stdio(bool sync = true);

Заключение
Какой механизм использовать — вопрос предпочтений программиста, если работодателем
явно не предписано использование конкретного механизма. В любом случае для физического
ввода-вывода используются вызовы операционной системы. Всё остальное — обёртка, набор
более или менее удобных функций или классов для взаимодействия с ОС.

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