- •Ввод-вывод в языке с
- •Потоковые функции
- •Открытие файлов и потоков
- •Переадресация ввода-вывода
- •Изменение буфера потока
- •Форматный вывод данных
- •Функция printf()
- •Поиск в файлах с помощью функций fseek( ), ftell( ) и rewind()
- •Синхронный и асинхронный ввод/вывод
- •3. Позиционирование указателя файла.
- •6. Запись данных в файл
- •Блокировка и разблокировка файла
- •Асинхронный режим чтения и записи файлов
- •Стандартные потоки cin, coutи cerr
- •Флаги и функции форматирования
- •Файловый ввод-вывод
- •Флаг Назначение
- •Файловый ввод
- •Файловый вывод
- •Вот результаты работы программы:
- •Двоичные файлы
- •Буферы потоков
ЛЕКЦИЯ: ВВОД-ВЫВОД
Ввод-вывод в языке с
Большинство популярных языков программирования высокого уровня имеет довольно ограниченные средства ввода-вывода. В результате программистам часто приходится разрабатывать изощренные алгоритмы, чтобы добиться необходимых результатов при вводе или выводе данных. К счастью, это не относится к языку С, который снабжен мощной подсистемой ввода-вывода, представленной обширной библиотекой соответствующих функций, хотя исторически эта подсистема не была частью языка. Как бы то ни было, те программисты, которые привыкли в языке Pascal использовать только два оператора — readln и writeln, — будут поражены обилием функций ввода-вывода в языке С.
Стандартные библиотечные функции ввода-вывода языка С позволяют считывать и записывать данные, связанные с файлами и различными устройствами. В то же время в самом языке С не предусмотрены какие-то предопределенные структуры файлов. Любые данные рассматриваются как цепочка байтов. В целом же все функции ввода-вывода можно разделить на три основные категории: потоковые, консольные и низкоуровневые.
Все потоковые функции воспринимают данные в виде потока символов. С их помощью можно передавать блоки символов определенного размера и формата, оперировать как отдельными символами, так и достаточно большими и сложными структурами данных.
На практике, когда программа открывает файл, используя потоковую функцию, организуется связь между файлом и структурой типа file, которая описана в файле STDIO.H и содержит основные сведения об открываемом файле. Одновременно программа получает указатель на эту структуру, называемый еще указателем потока или просто потоком. Данный указатель используется в дальнейшем при осуществлении всех операций ввода-вывода, связанных с этим файлом.
Потоковые функции обеспечивают возможность буферного, форматного и неформатного ввода-вывода. Буферные потоки позволяют временно сохранять данные в буфере как при записи данных в поток, так и при чтении их из потока. Поскольку прямая запись на диск и считывание с диска требуют много времени, использование в этих целях буферной области памяти значительно ускоряет процесс. Обмен данными потоковые функции всегда осуществляют не символ за символом, а целыми блоками. Если приложению необходимо прочесть блок информации, оно обращается прежде всего к буферу как к наиболее доступной области памяти. Если буфер оказывается пустым, то запрашивается очередной блок данных с диска. Обратный процесс происходит во время буферизованного вывода данных. Вместо того чтобы посылать устройству вывода всю информацию сразу, выводимые данные сначала записываются в буфер. Когда буфер оказывается заполненным, данные "выталкиваются" из него.
Следует учитывать, что многие языки высокого уровня испытывают проблемы с реализацией буферного ввода-вывода. Например, если программа выводит данные в буфер, но не заполняет его целиком (что привело бы к записи данных на диск), то эта информация будет утеряна после завершения программы.
Решение проблемы состоит в использовании специальных функций, которые "выталкивают" данные из буфера. В отличие от других языков высокого уровня, в С предусмотрена автоматическая выгрузка данных из буфера при завершении работы программы. Хотя, конечно, профессионально написанное приложение не должно полагаться на автоматическое решение этой проблемы: всегда следует явно указывать всю последовательность действий, которую должна выполнить программа для предотвращения потери данных. Еще одно замечание: если используются потоковые функции, то в случае аварийного завершения программы вся информация, хранящаяся в выходном буфере, может быть утеряна.
Ввод-вывод может также осуществляться через консоль или порт (например, порт принтера). Во втором случае соответствующие функции просто читают и записывают данные в байтах. Функции работы с консолью предоставляют ряд дополнительных возможностей. Например, можно определить момент ввода символа с клавиатуры, а также включить или отменить режим эха введенных символов на экране.
Последняя категория функций — низкоуровневые. Ни одна из них нe осуществляет промежуточной записи данных в буфер и какого бы то ни было форматирования. Все они напрямую используют системные средства ввода-вывода, открывая доступ к файлам и периферийным устройствам на более низком уровне, чем потоковые функции. При открытии файла с помощью низкоуровневых функций возвращается его дескриптор, который представляет собой целое число, употребляемое в дальнейших операциях в качестве идентификатора файла.
Стоит отметить, что довольно порочной практикой является смешение в одной программе потоковых и низкоуровневых функций. Поскольку потоковые функции заносят данные в буфер, а низкоуровневые — обращаются к данным напрямую, то последовательный доступ к одному и тому же файлу двумя функциями разного типа может привести к конфликтам и даже к потере данных в буфере.