В языке С определены три стандартных потока ввода/вывода:
Простейший ввод/вывод:
Все эти функции требуют включения заголовочного файла <conio.h>.
Все остальные функции ввода/вывода определены в заголовочном файле <stdio.h>.
Для ввода и вывода на экран используются функции scanf и printf соответственно, прототипы которых имеют следующий вид: int scanf (char *format, ...); int printf(char *format, ...);
Вывод осуществляется функцией printf, которая имеет следующий синтаксис: printf(<строка описания форматов> [, <список вывода>]);
Строка описания форматов состоит из обычных символов, специальных управляющих последовательностей символов и спецификаций формата.
Обычные символы и управляющие последовательности просто копируются в стандартный выходной поток в порядке их появления.
Спецификации формата начинаются с символа % и заканчиваются символом, определяющим тип выводимого значения. Кроме того, спецификации формата могут содержать символы и цифры для управления видом выводимого значения (подробно см. ниже). Список вывода состоит из переменных и/или констант, значения которых должны быть выведены. Количество спецификаций формата должно быть равно количеству выводимых значений, которые указываются в списке вывода. Если это условие не будет соблюдаться, выполнение функции может привести к непредсказуемым результатам.
Ввод осуществляется функцией scanf, которая имеет следующий синтаксис: scanf(<строка описания форматов> [, <список ввода>]);
Строка описания форматов состоит из набора спецификаций формата, таких же, как для функции printf. Список ввода состоит из адресов переменных, куда будут заноситься вводимые значения. Адрес переменной вычисляется с помощью унарной операции &. Количество спецификаций формата должно быть равно количеству вводимых значений, которые указываются в списке ввода.
Функция scanf возвращает количество успешно введенных и преобразованных значений. Функция printf возвращает количество символов, записанных в выходной поток.
К управляющим последовательностям относятся следующие последовательности символов.
Последовательность | Дейcтвие |
---|---|
\a | Звуковой сигнал |
\b | Удаление предыдущего символа |
\n | Новая строка |
\r | Возврат каретки |
\t | Табуляция |
\' | Апостроф |
\" | Кавычки |
\\ | Обратный слеш |
\ooo | ASCII символ в восьмеричной нотации |
\xooo | ASCII символ в шестнадцатеричной нотации |
Спецификация формата, которая состоит из обязательных и необязательных полей, имеет следующий вид:
%[флаги]
[ширина]
[.точность]
[{h | l | L | I64}]
тип
Флаги | Значение | По умолчанию |
---|---|---|
– | Выравнивание по левому краю. | Выравнивание по правому краю. |
+ | Добавление знака + или – перед числами. | Знак добавляется только перед отрицательными числами. |
0 | Добавление нулей перед выводимым значением. Если одновременно используются флаги – и 0, 0 игнорируется. | Добавление пробелов. |
пробел | Добавление пробела перед положительным числом. Если одновременно используются флаги пробел и +, пробел игнорируется. | Пробел не добавляется. |
# | Добавление символов 0, 0х или 0Х перед ненулевым значением, если флаг # используется с форматами о, х или Х соответственно. | Символы 0, 0х и 0Х не добавляются. |
При использовании с форматами e, E и f флага # выводимое число будет содержать десятичную точку в любом случае. | Десятичная точка добавляется, только если за ней следуют цифры. | |
При использовании с форматами g и G флага # выводимое число будет содержать десятичную точку и хвостовые нули в любом случае. | Десятичная точка добавляется, только если за ней следуют цифры. Хвостовые нули не выводятся. | |
Игнорируется при использовании с форматами c, d, i, u и s. |
Поле ширина содержит минимальное количество выводимых символов – неотрицательное целое число. Если выводимое значение содержит меньше символов, то оно расширяется пробелами (если не задан флаг 0). Если поле ширина содержит звездочку (*), то в качестве значения поля берётся целое число из списка аргументов, предшествующее выводимому значению.
Поле точность также представляет собой неотрицательное целое число. Действие зависит от типа выводимого значения.
Тип | Действие | По умолчанию |
---|---|---|
c, C | Точность не имеет эффекта. | Выводится символ. |
d, i, u, o, x, X | Точность задаёт минимальное количество символов, которые будут напечатаны. Если число содержит меньше символов, оно расширяется нулями. | Точность равна 1. |
e, E, f | Точность задаёт количество символов после десятичной точки. Число округляется. | Точность равна 6. Если точность равна 0 или опущена, десятичная точка не выводится. |
g, G | Точность задаёт максимальное количество значащих цифр. | Печатается 6 значащих цифр. |
s, S | Точность задаёт максимальное количество выводимых символов. | Выводятся все символы строки. |
Если поле точность содержит звездочку (*), то в качестве значения поля берётся целое число из списка аргументов, предшествующее выводимому значению.
Дополнительные префиксы h, l, L и I64 задают «размер» аргумента – long или short, однобайтовый символ или расширенный символ, в зависимости от спецификации типа, которую они модифицируют.
Обязательное поле тип задаёт тип выводимого значения.
Символ | Тип | Формат вывода |
---|---|---|
c | int или wint_t | При использовании с функцией printf определяет однобайтовый символ, при использовании с функцией wprintf определяет расширенный символ. |
C | int или wint_t | При использовании с функцией printf определяет расширенный символ, при использовании с функцией wprintf определяет однобайтовый символ. |
d | int | Знаковое десятичное целое. |
i | int | Знаковое десятичное целое. |
o | int | Беззнаковое восьмеричное целое. |
u | int | Беззнаковое десятичное целое. |
x | int | Беззнаковое шестнадцатеричное целое с использованием символов «abcdef». |
X | int | Беззнаковое шестнадцатеричное целое с использованием символов «ABCDEF». |
e | double | Знаковое число в форме [ – ]d.dddd e [знак]ddd, где d есть одна десятичная цифра, dddd – одна или более десятичных цифр, ddd – три десятичные цифры and знак есть + или –. |
E | double | Идентичен формату e, за исключением того, что символ E, а не e вводит экспоненту. |
f | double | Знаковое число в форме [ – ]dddd.dddd, где dddd есть одна или более десятичных цифр. Количество цифр перед десятичной точкой зависит от величины числа, а количество цифр после десятичной точки – от требуемой точности. |
g | double | Знаковое число в формате f или e, в зависимости от того, какой формат более компактен для заданного значения и точности. |
G | double | Идентичен формату g, за исключением того, что символ E, а не e вводит экспоненту. |
n | pointer to integer | Количество символов успешно записанных к данному моменту в выходной поток. Это значение сохраняется в целочисленной переменной, чей адрес задан как аргумент. |
p | pointer to void | Печатает адрес, заданный аргументом. |
s | string | При использовании с функцией printf задаёт строку однобайтовых символов, при использовании с функцией wprintf задаёт строку расширенных символов. Символы печатаются до достижения признака конца строки. |
S | string | При использовании с функцией printf задаёт строку расширенных символов, при использовании с функцией wprintf задаёт строку однобайтовых символов. Символы печатаются до достижения признака конца строки. |
int m, n, x; double y; char c = '&'; char str[] = "String"; | |
scanf("%d%d", &m, &n); | // Ввод десятичных целых чисел в переменные m и n |
printf("m = %5d\nn = %5d\n", m, n); | // Вывод переменных m и n в десятичном целом формате, используются как минимум 5 знаков |
scanf("%d", &x); | // Ввод десятичного целого числа в переменную x |
printf("%#010x\n", x); | // Вывод переменной x в шестнадцатеричной системе, используются 10 знаков, // впереди добавляются нули и символы 0х |
scanf("%lf", &y); | // Ввод вещественного числа в переменную y |
printf("y = %7.2lf\n", y); | // Вывод вещественной переменной, используются как минимум 7 знаков, из них 2 – после точки |
printf("c = %c\n", c); | // Вывод одного символа |
printf("%.4s\n", str); | // Вывод строки (не более 4 символов) |
Функции sprintf и sscanf позволяют произвести запись значений переменных в форматированную строку или чтение переменных из строки: int sscanf (char *str, char *format, ...); int sprintf(char *str, char *format, ...);
Эти функции во всём аналогичны функциям printf и scanf, только в качестве первого параметра указывается строка, куда записываются или откуда считываются данные.
|
– FILE *file; |
|
– FILE *fopen(char *name, char *mode); |
|
– int feof(FILE *file); |
|
– int fclose(FILE *file); |
FILE – специальная структура, объявленная в файле <stdio.h>, которая используется при работе с файлами. Для работы с файлом нужно объявить переменную FILE *<имя>.
Функция fopen используется для открытия файла. Первый параметр задаёт имя файла. Второй параметр mode задаёт требуемый тип доступа к файлу.
Mode | Действие |
---|---|
"r" | Открытие для чтения. Если файл не существует или не может быть найден, функция fopen возвращает признак ошибки. |
"w" | Открытие для записи. Если файл существует, его содержимое уничтожается. Если файл не существует, он создаётся. |
"a" | Открытие для добавления. Если файл не существует, он создаётся. |
"r+" | Открытие для чтения и записи. Файл должен существовать. |
"w+" | Открытие пустого файла для чтения и записи. Если файл существует, его содержимое уничтожается. |
"a+" | Открытие для чтения и добавления. Если файл не существует, он создаётся. |
Кроме того, к параметру mode могут быть добавлены символы t и b для задания текстового и двоичного режимов соответственно. По умолчанию используется текстовый режим.
В случае ошибки функция fopen возвращает значение NULL.
При вводе/выводе в текстовом режиме происходит преобразование между внешним представлением значения и внутренним (машинным) представлением этого значения.
|
– int getc (FILE *file); |
|
– int putc (int c, FILE *file); |
|
– int fscanf (FILE *file, char *format, ...); |
|
– int fprintf(FILE *file, char *format, ...); |
|
– char* fgets (char *line, int maxline, FILE *file); |
|
– int fputs (char *line, FILE *file); |
В двоичном режиме никаких преобразований не производится, внутреннее представление значения записывается в файл. Для открытия файла в двоичном режиме необходимо в параметр mode функции fopen добавить символ b.
|
– unsigned fread (void *buf, int bytes, int num, FILE *file); |
|
– unsigned fwrite (void *buf, int bytes, int num, FILE *file); |
Функция fread читает из файла file в переменную buf num элементов, каждый размером bytes байт. Функция fwrite записывает в файл file из переменной buf num элементов, каждый размером bytes байт. Функции возвращают количество прочитанных/записанных элементов.
В двоичном режиме возможен прямой доступ к файлу: int fseek(FILE *file, long nbytes, int origin)
Данная функция смещает указатель в файле file на nbytes байт с позиции, определяемой параметром origin. При этом параметр origin может принимать следующие значения:
Функция long ftell(FILE *file) возвращает текущую позицию указателя в файле file.
fseek(file, 0, SEEK_END); n = ftell(file); | // Определение размера файла |
В языке С++ был разработан другой способ ввода/вывода с использованием так называемых потоков ввода и вывода.
Для того чтобы использовать стандартные потоки для ввода и вывода, необходимо включить заголовочный файл <iostream>. Для ввода используется операция >>, для вывода – операция <<. Компилятор определяет тип вводимой/выводимой переменной и соответствующим образом форматирует её.
#include <iostream>
using namespace std; |
|
cin >> x; | // Ввод значения в переменную x из стандартного потока cin |
cout << x; | // Вывод значения переменной x в стандартный поток cout |
cin >> x >> y; | // Ввод двух переменных |
cout << "x = " << x << "\ny = " << y << endl; | // Функция endl осуществляет перевод строки |
Если при вводе или выводе произошла ошибка, в переменной состояния потока устанавливается соответствующий флаг. Проверить его значение можно с помощью функции fail.
cin >> x; if (cin.fail()) cout << "Произошла ошибка при вводе\n"; |
Для управления форматом вводимого/выводимого значения используются так называемые манипуляторы. Это функции, которые вставляются между вводимыми/выводимыми значениями и изменяют состояние потока.
Для использования манипуляторов необходимо включить заголовочный файл <iomanip>.
Несколько манипуляторов имеют параметр, который может быть задан литералом или переменной. Изменения, сделанные всеми манипуляторами, кроме setw, остаются в силе до отмены. Действие манипулятор setw распространяется только на одно вводимое/выводимое значение.
Манипулятор | Описание | Примечание |
---|---|---|
boolalpha | Значения переменных типа bool выводятся как true и false. | |
dec | Целые значения выводятся в десятичной системе счисления. | Используется по умолчанию |
fixed | Для вещественных чисел используется фиксированный формат. | |
hex | Целые значения выводятся в шестнадцатеричной системе счисления. | |
internal | Знак выравнивается по левому краю, а само число – по правому краю. | |
left | Выравнивание по левому краю. | |
noboolalpha | Значения переменных типа bool выводятся как 1 и 0. | Используется по умолчанию |
noshowbase | Префиксы 0 и 0х, обозначающие систему счисления, не выводятся. | Используется по умолчанию |
noshowpoint | Вывод только целой части вещественного числа (без точки), если дробная часть равна 0. | Используется по умолчанию |
noshowpos | Знак перед положительными числами не выводится. | Используется по умолчанию |
noskipws | Пробел рассматривается как признак завершения ввода. | |
nouppercase | Шестнадцатеричные цифры и символ экспоненты в научном формате вещественного числа выводятся строчными буквами. | Используется по умолчанию |
oct | Целые значения выводятся в восьмеричной системе счисления. | |
right | Выравнивание по правому краю. | Используется по умолчанию |
scientific | Для вещественных чисел используется научный формат. | |
setfill(c) | Задаёт символ для заполнения. По умолчанию используется пробел. | |
setprecision(n) | Задаёт точность для вещественных чисел. По умолчанию точность равна 6. Если не установлен ни фиксированный, ни научный формат вещественного числа, то точность задаёт количество выводимых цифр (всего, до точки и после точки). Если число слишком велико, оно автоматически отображается в научном формате, и тогда точность задаёт количество цифр в мантиссе. Если установлен фиксированный формат вещественного числа, точность задаёт количество цифр после точки. Если установлен научный формат вещественного числа, точность задаёт количество цифр в мантиссе. | |
setw(n) | Устанавливает минимальное количество символов, используемых для вывода значения. Если значение представляется меньшим количеством символов, остальные позиции заполняются символом, установленным с помощью манипулятора setfill. Выравнивание задаётся манипуляторами left, right и internal. Чтобы установить поведение по умолчанию (столько символов, сколько необходимо), нужно использовать манипулятор setw с параметром 0. | Влияет только на одно вводимое/выводимое значение! |
showbase | Вывод префиксов 0 и 0х для обозначения системы счисления. | |
showpoint | Вывод и целой, и дробной частей вещественного числа, даже если дробная часть равна 0. | |
showpos | Вывод знака перед положительным числом. | |
skipws | Пробелы рассматриваются как разделители между значениями. | Используется по умолчанию |
uppercase | Шестнадцатеричные цифры и символ экспоненты в научном формате вещественного числа выводятся прописными буквами. |
int m, n, x; double y; cin >> m >> n; cout << "m = " << setw(5) << m << "\nn = " << setw(5) << n << endl; cin >> x; cout << setfill('0') << showbase << hex << setw(10) << internal << x << endl; cin >> y; cout << setfill(' ') << fixed << setw(7) << setprecision(2) << y << endl; |
Рассмотрим пример.
char c;
cout << "Введите символ: ";
cin >> c;
Как нам гарантировать, что слова Введите символ появятся на экране прежде, чем будет выполнена операция считывания? Вывод в стандартный поток буферизуется, так что если потоки cin и cout не зависимы, то выводимый текст не появится на экране, пока не заполнится буфер вывода. Решение этой задачи заключается в том, что потоки связываются с помощью функции tie. Эта функция используется для того, чтобы устанавливать и разрывать связи между потоками ввода и вывода.
char c;
cin.tie(&cout);
cout << "Введите символ: ";
cin >> c;
В каждый момент времени поток ввода может быть связан только с одним потоком вывода. Обращение s.tie(0) разрывает связь между потоком s и потоком, с которым он был связан. Как и большинство функций с потоками, устанавливающих значение, функция tie возвращает прежнее значение, т.е. она возвращает предыдущий связанный поток или 0. При вызове без аргументов функция tie() возвращает текущий связанный поток, не изменяя его.
Для ввода/вывода из файла/в файл существуют потоки, которые могут быть связаны с файлом на диске. Для использования файловых потоков необходимо включить заголовочный файл <fstream>. Существует три разновидности файловых потоков: fstream, ifstream и ofstream. Разница между ними состоит в том, что поток fstream по умолчанию открывается для ввода и вывода, поток ifstream по умолчанию открывается для ввода, а поток ofstream по умолчанию открывается для вывода. Изменить поведение по умолчанию, а также задать другие режимы открытия файла можно с помощью следующих констант:
Режимы открытия файла комбинируются с помощью операции поразрядного ИЛИ (|).
Для открытия файла можно задать имя файла непосредственно в конструкторе потока или воспользоваться функцией open.
fstream fs("f1.txt"); | // Открытие файла для чтения и записи |
ifstream ifs("f2.txt"); | // Открытие файла для чтения |
ofstream ofs("f3.txt"); | // Открытие файла для записи |
fstream fs("f1.txt", ios_base::in | ios_base::out | ios_base::trunk); | // Открытие файла для чтения и записи с удалением содержимого файла |
ifstream ifs("f2.txt", ios_base::in | ios_base::binary); | // Открытие двоичного файла для чтения |
ofstream ofs; | // Создаём поток, не связанный с файлом |
ofs.open("f3.txt"); | // Открываем файл для записи |
Если вы планируете использовать файл только для чтения или только для записи безопаснее воспользоваться соответствующим файловым потоком.
Для проверки открытия файла служит функция is_open.
Потоки автоматически закрываются при завершении программы. Однако при необходимости можно закрыть поток функцией close и затем снова открыть его, связав с другим файлом.
Для работы с текстовыми потоками используются операции << и >>. Также возможно использование манипуляторов для форматирования вводимых/выводимых значений.
int x; fstream f; | |
f.open("in.txt", ios_base::in); | // Открываем файл для чтения |
if (!f.is_open()) | // Проверяем открытие файла |
{ cout << "Невозможно открыть файл 'in.txt'\n"; return; } | |
f >> x; | // Чтение переменной x из файла |
if (f.fail()) | // Проверка ошибок чтения |
{ cout << "Ошибка чтения из файла 'in.txt'\n"; return; } | |
f.close(); | // Закрываем файл |
f.open("out.txt", ios_base::out); | // Снова открываем файл, теперь для записи |
if (!f.is_open()) | // Проверяем открытие файла |
{ cout << "Невозможно открыть файл 'out.txt'\n"; return; } | |
f << hex << x << endl; | // Выводим значение переменной x в 16-ричной системе |
f.close(); | // Закрываем файл |
Для того чтобы проверить, достигнут ли конец файла, используется функция eof.
int n; ifstream f("in.txt"); | |
if (!f.is_open()) | |
{ cout << "Невозможно открыть файл 'in.txt'\n"; return; } | |
while (!f.eof()) | // Пока не достигнут конец файла |
{ f >> n; cout << n << endl; } |
Для организации прямого доступа к файлу используются функции seekg/seekp и tellg/tellp. Различие между функциями состоит в том, что функции, с именем, оканчивающимся символом ‘g’, используются для работы с потоками ввода, а функции, с именем, оканчивающимся символом ‘p’, – для работы с потоками вывода.
Функции seekg/seekp перемещают внутренний указатель файла на заданную позицию. Позиции соответствуют байтам, нумерация начинается с 0. Существует две разновидности функций – с одним параметром и с двумя параметрами. Один целочисленный параметр задаёт абсолютную позицию в файле. Два параметра задают смещение (целое число) и точку отсчёта. Этот параметр может принимать следующие значения:
Функции tellg/tellp не имеют параметров. Они возвращают текущую позицию указателя в файле.
Функции seekg/seekp и tellg/tellp работают как с текстовыми, так и с двоичными потоками. В любом случае желательно либо знать структуру файла, либо работать с файлами, все записи в которых имеют одинаковую длину. В противном случае возможно перемещение указателя на позицию, не являющуюся началом записи.
Для работы с двоичными файлами используются функции read и write. В качестве параметров функции получают указатель (типа char* для функции read и типа const char* для функции write), который задаёт адрес начала массива для ввода/вывода, и целое число, задающее количество байт для ввода/вывода.
Пример использования потокового ввода из двоичного файла см. в конце лекции.
// Функция ввода одномерного массива.
// Если ввод был осуществлен без ошибок, возвращается 1, в противном случае - 0.
// x - вводимый массив,
// n - указатель на переменную, содержащую количество элементов массива,
// fname - имя файла для ввода.
int ArrayInput(double x[], int *n, char *fname)
{ int i;
FILE *file;
if ((file = fopen(fname, "r")) == NULL)
{ printf("Невозможно открыть файл '%s'\n", fname);
return 0;
}
if (fscanf(file, "%d", n) < 1)
{ printf ("Ошибка чтения из файла '%s'\n", fname);
fclose(file);
return 0;
}
if (*n < 0 || *n > NMAX)
{ printf("Кол-во эл-тов масс. должно быть от 1 до %d! (файл '%s')\n", NMAX, fname);
fclose(file);
return 0;
}
for (i = 0; i < *n; i++)
if (fscanf(file, "%lf", &x[i]) < 1)
{ printf ("Ошибка чтения из файла '%s'\n", fname);
fclose(file);
return 0;
}
fclose(file);
return 1;
}
// Вывод массива в двоичный файл.
// Если вывод был осуществлен без ошибок, возвращается 1, в противном случае - 0.
// x - выводимый массив,
// n - переменная, содержащая количество элементов массива,
// fname - имя файла для вывода.
int BinOutput(double x[], int n, char *fname)
{ FILE *file;
if ((file = fopen(fname, "wb")) == NULL)
{ printf("Невозможно открыть файл '%s'\n", fname);
return 0;
}
if (fwrite(x, n * sizeof(double), 1, file) < 1)
{ printf ("Ошибка записи в файл '%s'\n", fname);
fclose(file);
return 0;
}
fclose(file);
return 1;
}
// Ввод массива из двоичного файла.
// Если ввод был осуществлен без ошибок, возвращается 1, в противном случае - 0.
// x - вводимый массив,
// n - указатель на переменную, содержащую количество элементов массива,
// fname - имя файла для ввода.
int BinInput(double x[], int *n, char *fname)
{ FILE *file;
if ((file = fopen(fname, "rb")) == NULL)
{ printf("Невозможно открыть файл '%s'\n", fname);
return 0;
}
for (*n = 0; ; (*n)++)
if (fread(x + (*n), sizeof(double), 1, file) < 1)
if (feof(file))
break;
else
{ printf ("Ошибка чтения из файла '%s'\n", fname);
fclose(file);
return 0;
}
fclose(file);
return 1;
}
// Другой способ
int BinInput(double x[], int *n, char *fname)
{ FILE *file;
if ((file = fopen(fname, "rb")) == NULL)
{ printf("Невозможно открыть файл '%s'\n", fname);
return 0;
}
fseek(file, 0, SEEK_END);
*n = ftell(file) / sizeof(double);
fseek(file, 0, SEEK_SET);
if (fread(x, sizeof(double), *n, file) < *n)
{ printf ("Ошибка чтения из файла '%s'\n", fname);
fclose(file);
return 0;
}
fclose(file);
return 1;
}
// Ввод массива из двоичного файла.
// Если ввод был осуществлен без ошибок, возвращается 1, в противном случае - 0.
// x - вводимый массив,
// n - указатель на переменную, содержащую количество элементов массива,
// fname - имя файла для ввода.
int BinInput(double x[], int *n, char *fname)
{ ifstream f(fname, ios_base::in | ios_base::binary);
if (!f.is_open())
{ cout << "Невозможно открыть файл '" << fname << "'\n";
return 0;
}
f.seekg(0, ios_base::end);
*n = f.tellg() / sizeof(double);
f.seekg(0, ios_base::beg);
f.read(reinterpret_cast<char *>(x), *n * sizeof(double));
if (f.fail())
{ cout << "Ошибка чтения из файла '" << fname << "'\n";
return 0;
}
return 1;
}