Ввод и вывод в языке C Одной из основных особенностей языка Си по отношению к большинству других языков программирования высокого уровня является полное отсутствие в нем предопределенных операторов ввода/вывода Это, однако, ни в коей мере не ограничивает его возможностей по организации больших наборов данных и управлению периферийным оборудованием, ибо все необходимые для этого средства предоставляются функциями из стандартных библиотек языка Си Более того, подобное отношение к операциям ввода/вывода открывает путь к созданию высоко эффективного и мобильного программного обеспечения, максимально использующего возможности операционной системы по поддержанию внешнего обмена
При разработке и эксплуатации программного обеспечения операции ввода/вывода играют столь же большую роль, как, например, инструкции управления или способы описания оперативных структур данных всякая программа, лишенная возможности общения с "внешним миром", оказывается либо недостаточно гибкой по отношению к наборам обрабатываемых ею данных, либо абсолютно бесполезной по причине отсутствия возможности сообщить пользователю о результатах своей работы во многих прикладных задачах объемы перерабатываемой информации столь велики, что не может быть и речи о ее одновременном размещении в оперативной памяти и возникает прямая необходимость в непрерывной "подкачке" данных в процессе работы программы использование одних лишь структур данных, расположенных в оперативной памяти, не обеспечивает сохранения информации после выключения питания компьютера ликвидируя тем самым возможность ведения долгосрочных архивов и создания информационных баз
В современных вычислительных системах внешние устройства, с которыми приходится поддерживать обмен, по выполняемым функциям делятся на две категории - всевозможные устройства для связи оператора с компьютером: интерактивные терминалы и телетайпы, принтеры, графопостроители, фотонаборные устройства и т. д. - накопители информации на магнитных носителях, каковыми чаще всего выступают магнитные диски Две эти категории периферийных устройств принципиально отличаются тем, что в первом случае передача данных на устройство осуществляется последовательным образом (потоком) и носит, как правило, однонаправленный характер Во втором же случае обычно имеет место двусторонняя передача информации и достаточно сложная логическая организация данных, поддерживаемая специальными компонентами операционной системы и прикладными программами
Файлы данных и каталоги файлов Файлами принято называть поименованные наборы данных на внешних носителях Совокупность файлов обычно организуется по иерархическому принципу в виде файлового дерева, чем обеспечивается удобство представления и обработки больших информационных структур По характеру хранимой информации различают файлы данных и файлы-каталоги (директории) Первые из них содержат данные, непосредственно используемые прикладными программами, включая исходные тексты, объектные и загрузочные модули самих этих программ Наоборот, содержимое каталогов составляют указатели на другие файлы и подкаталоги
Прикладные программы лишены возможности непосредственно вмешиваться в структуру каталогов, а могут делать это лишь опосредованно путем обращения к соответствующим функциям операционной системы Тот каталог, который находится на высшем уровне иерархической организации, носит название корневого каталога Логическая организация файлов данных в каталоги поддерживается файловой системой, входящей в состав ОС. Основное назначение файловой системы состоит в том, чтобы отделить пользователя компьютера от излишних деталей внешнего обмена и предоставить ему возможность простого и гибкого управления большими информационными структурами Именами файлов и каталогов в Си-программах могут быть любые правильные имена операционной системы. В отдельных случаях обеспечивается возможность использования специальных метасимволов для генерации последовательности имен
В операционной среде WINDOWS не существует какой-либо предопределенной внутренней структуры файлов Всякий файл представляет собой простую последовательность байт, каждый из которых может содержать либо правильный код символа ASCII, либо быть произвольной комбинацией двоичных разрядов Файлы первого типа обычно называют текстовыми файлами и они могут быть визуализированы, например, на экране видеотерминала Файлы же второго типа, называемые двоичными файлами, могут содержать внутри себя непечатаемые символы или управляющие последовательности и в общем случае не подлежат визуальному просмотру Заметим, однако, что с точки зрения внутренней обработки информации, не существует принципиальной разницы между текстовыми и двоичными файлами
Тем не менее, есть определенные отличия в интерпретации содержимого файлов некоторыми стандартными функциями при их использовании в текстовом и двоичном режимах Так, например, в текстовом режиме комбинация символов CR/LF (hex-коды 0xD и 0xA соответственно) преобразуется при вводе в единственный символ LF. В процессе же вывода выполняется обратное преобразование. При работе в двоичном режиме подобные преобразования не выполняются
Стандартные функции для работы с файлами и каталогами Приведем наиболее употребительные функции управления вводом/выводом из стандартной библиотеки языка Си. По своему функциональному назначению они разбиты на три категории ввод/вывод потоком символов операции обмена низкого уровня управление файлами и каталогами Организация обмена со стандартными внешними устройствами рассмотрена в следующем параграфе
1. Ввод/вывод потоком символов Функции ввода/вывода потоком рассматривают файлы и элементы данных как последовательности отдельных символов. Они предоставляют возможность буферизации информации при пересылке, а также обеспечивают преобразование данных в соответствии с заданной спецификацией формата Всякий поток перед обращением к нему должен быть открыт при помощи функции fopen для ввода, вывода или выполнения обеих этих операций в текстовом или двоичном режиме. Открывая поток, функция fopen возвращает указатель типа FILE, который используется для ссылки на него во всех операциях ввода/вывода или управления указателем текущей позиции После завершения работы с потоком, его следует закрыть, используя функцию fclose. В противном случае это сделает операционная система по окончании работы программы
Операции чтения и записи для потока выполняются, начиная с текущей позиции указателя, смещая последний на соответствующее число позиций Используя функцию fseek, указатель можно установить в произвольную позицию файла перед выполнением очередной операции, а функция rewind позиционирует указатель на начало файла Предварительные описания функций ввода/вывода потоком, а также определения соответствующих констант, типов и структур помещены в файл stdio.h, который необходимо включить в программу при помощи директивы препроцессора #include. В этом же файле описаны некоторые полезные константы EOF - определение значения возврата при достижении конца файла NULL - указатель на нуль FILE - структура, которая содержит информацию о потоке BUFSIZ - размер буфера в байтах
Имя функции и назначение: fopen - открывает файл с заданным именем для ввода/вывода потоком Формат и описание аргументов: FILE *fopen(pathname, type) const char *pathname; /* Имя открываемого файла */ const char *type; /* Тип доступа к файлу */ Параметр type представляет собой строку символов и определяет характер доступа к файлу: "r" - существующий текстовый файл открыть для чтения (файл должен существовать) "w" - текстовый файл открыть для записи (в случае существования файла его содержимое разрушается) "a" - текстовый файл открыть для записи в конец файла (добавление) (если файл не существует он создается) "r+" - существующий файл открыть для чтения и записи (файл должен уже существовать) "w+" - пустой текстовый файл открыть для чтения и записи (в случае существования файла его содержимое разрушается)
Добавление символа "b" в конец каждой из этих строк позволяет открыть двоичный файл для выполнения соответствующих операций Функция возвращает указатель типа FILE при нормальном завершении операции и NUL L в случае возникновения ошибки Пример использования: #include void main() { FILE *stream; if ((stream = fopen("data", "r")) == NULL) printf("Ошибка при открытии файла"); }
Имя функции и назначение: fclose - закрывает файл, предварительно открытый для ввода/вывода потоком, fcloseall() - закрывает все потоки Формат и описание аргументов: int fclose(stream) FILE *stream; /* Указатель на открытый файл */
Пример использования: #include void main() { FILE *stream; stream = fopen("data", "w+"); fclose(stream); }
Имя функции и назначение: feof - проверяет возникновение ситуации "конец файла" при вводе/выводе потоком Формат и описание аргументов: int feof(stream) FILE *stream; /* Указатель на открытый файл */ Возвращаемое значение отлично от нуля при попытке чтения за пределами файла и равно нулю в противном случае
Пример использования: #include char string[100]; FILE *stream; void process(char*); void main() { while (!feof(stream)) if (fscanf(stream, "%s", string))process(string); }
Имя макроса и назначение: getc(stream) - читает очередной символ из входного потока и увеличивает значение указателя getchar() - аналогично, но с потоком из стандартного ввода Имя функции и назначение: fgetc - читает очередной символ из входного потока и увеличивает значение указателя. То же, что и getc и getchar, но являются функциями, а не макросами Формат и описание аргументов: int fgetc(stream) FILE *stream; /* Указатель на открытый файл */ Возвращаемое значение равно коду прочитанного символа при нормальном завершении операции и EOF в случае возникновения ошибки или при достижении конца файла
Пример использования: #include void main() { char buffer[81]; int i, ch; FILE *stream; for (i=0; i
Формат и описание аргументов: char *fgets(string, n, stream) char *string; int n; FILE *stream; /* Указатель на открытый файл */ Возвращаемое значение равно указателю на строку string при нормальном завершении операции и NULL в случае возникновения ошибки или при достижении конца файла Пример использования: #include void main() { char line[100], *result; FILE *stream;... result = fgets(line, 100, stream);... }
Имя функции и назначение: getw - читает из входного потока очередное целое число, заданное в двоичном формате, и увеличивает значение указателя Формат и описание аргументов: int getw(stream) FILE *stream; /* Указатель на открытый файл */ Возвращаемое значение равно прочитанному целому числу при нормальном завершении операции и EOF в случае возникновения ошибки или при достижении конца файла
Пример использования: #include void main() { int ivalue; FILE *stream;... ivalue = getw(stream);... }
Имя функции и назначение: fputc - записывает символ в выходной поток и увеличивает значение указателя Формат и описание аргументов: int fputc(c, stream) int c; /* Выводимый символ */ FILE *stream; /* Указатель на открытый файл */ Возвращаемое значение равно коду записанного символа при нормальном завершении операции и EOF в случае возникновения ошибки
Пример использования: #include void main() { char buffer[81]; int i; FILE *stream;... for (i = 0; i < 81 && buffer[i] != '\0'; i++) fpufc(buffer[i], stream);... }
Имя функции и назначение: fputs - копирует строку символов в выходной поток Формат и описание аргументов: int fputs(string, stream) const char *string; FILE *stream; Возвращаемое значение равно коду последнего символа строки при нормальном завершении операции, нулю для пустой строки и EOF в случае возникновения ошибки
Пример использования: #include void main() { FILE *stream;... fputs("This is a string", stream);... }
Имя функции и назначение: putw - записывает в выходной поток двоичное представление целого числа и увеличивает значение указателя Формат и описание аргументов: int putw(binint, stream) int binint; FILE *stream; /* Указатель на открытый файл */ Возвращаемое значение равно binint при нормальном завершении операции и EOF в случае возникновения ошибки
Пример использования: #include void main() { FILE *stream;... putw(0347, stream);... }
Имя функции и назначение: fscanf - читает данные из входного потока и заносит их в ячейки памяти, определяемые аргументами функции Формат и описание аргументов: int fscanf(stream, fstring ) FILE *stream; /* Указатель на открытый файл */ const char *fstring; /* Строка управления форматом */ Строка string управления форматом определяет способ интерпретации входных данных. Она может содержать следующие поля: пробелы, символы табуляции ( \t ) или перехода на новую строку ( \n ), которые позволяют читать без сохранения любые комбинации соответствующих символов из входного потока; произвольные печатаемые символы ASCII, кроме знака процента (%), использование которых позволяет читать без сохранения соответствующие им символы из входного потока; спецификации формата, начинающиеся со знака процента (%) и требующие преобразования прочитанных данных к заданному типу
Аргументы функции fscanf являются указателями на ячейки памяти, куда необходимо поместить прочитанные значения, и должны иметь тот же самый тип, который задан соответствующими спецификациями в строке управления форматом Всякая спецификация формата в общем случае имеет следующий вид: % type где угловыми скобками ( ) обозначены необязательные элементы конструкции. Отдельные поля спецификации могут быть символами или числами, задающими конкретный способ преобразования данных при вводе Символ преобразования, соответствующий полю type, определяет тип очередного элемента данных, читаемого из входного потока. Этим элементом может быть отдельный символ ASCII, строка символов или число (целое или вещественное). Ниже приведена таблица символов преобразования, а также указан тип отвечающего каждому из них аргумента
СимволЭлемент входного потокаТип аргумента d десятичное целоеуказатель на на int D десятичное целоеуказатель на long o восьмеричное целоеуказатель на на int O восьмеричное целоеуказатель на long X шестнадцатеричное целоеуказатель на на int X шестнадцатеричное целоеуказатель на long i десятичное, восьмеричное или шестнадцатеричное целое указатель на на int I десятичное, восьмеричное или шестнадцатеричное целое указатель на long u десятичное целое без знакауказатель на unsigned int U десятичное целое без знакауказатель на unsigned long e, f, g, E вещественное числоуказатель на float c символ ASCIIуказатель на char s строка символовуказатель на массив элементов типа char p значение вида xxxx:yyy где x и y есть шестнадцатеричные цифры указатель на адрес
Символ звездочка (*), возможно входящий в спецификацию формата, заставляет систему обмена проигнорировать очередной элемент данных заданного типа во входном потоке Необязательное поле width представляет собой положительное десятичное число, задающее максимальное количество символов, читаемых из входного потока и интерпретируемых в соответствии со спецификатором type Дополнительные спецификаторы h, l и L могут быть использованы для модификации типа аргумента, определенного полем type. Их действие распространяется лишь на длину соответствующего аргумента ( short или long ) Возвращаемое функцией fscanf значение равно количеству прочитанных и преобразованных в соответствии с заданным форматом полей входного потока. При попытке чтения за пределами файла функция возвращает значение EOF
Пример использования: #include void main() { char sym, string[81]; int ival; long lval; float fval; FILE *stream; stream = fopen("data", "r"); fscanf(stream, "%c", &sym); fscanf(stream, "%s", string); fscanf(stream, "%d", &ival); fscanf(stream, "%ld", &lval); fscanf(stream, "%f", &fval); }
Имя функции и назначение: fprintf - записывает форматированные данные в выходной поток Формат и описание аргументов: int fprintf(stream, fstring ) FILE *stream; /* Указатель на открытый файл */ const char *fstring; /* Строка управления форматом */ Строка fstring управления форматом определяет способ преобразования данных при выводе. Она может включать в себя обычные символы, копируемые в выходной поток, Esc- последовательности и спецификации формата, начинающиеся со знака процента (%). Аргументы функции fprintf являются простыми переменными или указателями, задающими набор выводимых значений.
Всякая спецификация формата в общем случае имеет следующий вид: % type где угловыми скобками ( ) обозначены необязательные элементы конструкции. Отдельные поля спецификации могут быть символами или числа ми, задающими конкретный способ преобразования данных при выводе Символ преобразования, соответствующий полю type, определяет тип очередного элемента данных, записываемого в выходной поток. Этим элементом может быть отдельный символ ASCII, строка символов или число (целое или вещественное). Ниже приведена таблица символов преобразования, имеющих специальное значение
СимволАргументФормат вывода d, I целое числодесятичное целое со знаком u целое числодесятичное целое без знака o целое числовосьмеричное целое без знака x целое числошестнадцатеричное целое без знака, использующее abcdef X целое числошестнадцатеричное целое без знака, использующее ABCDEF f вещественное числовещественное целое со знаком с фиксированной точкой e,E вещественное числовещественное целое со знаком в экспоненциальной форме g,G вещественное числовещественное целое со знаком с фиксированной точкой или в экспоненциальной форме в зависимости от величины порядка c cимвол ASCIIотдельный символ s указатель типа charстрока символов
Символы, занимающие поле flags в строке формата, позволяют управлять размещением выводимой информации в пределах поля, заданного параметром width, а также определяют способ вывода чисел со знаком. Так, знак минус (-) в этом случае требует выравнивания печатаемых символов по левой границе поля width (по умолчанию производится выравнивание по правой границе), а знак плюс (+) заставляет выводить со знаком (+ или -) как положительные, так и отрицательные числа Дополнительный флаг в виде знака номера (#) может использоваться для вывода начального нуля в восьмеричном формате и пары 0x или 0X в шестнадцатеричном формате, а также для обязательной печати десятичной точки при выводе вещественных чисел даже при нулевом значении поля precision Параметр width является целым положительным десятичным числом, определяющим минимальное количество позиций, отводимых под соответствующий элемент данных в выходном потоке. Если количество символов в выводимом значении меньше width, то осуществляется его расширение пробелами слева или справа в зависимости от значения поля flags
Для замены пробелов на нули необходимо снабдить параметр width префиксом нуль ( 0 ). При отсутствии этого параметра в строке формата он автоматически получает некоторое стандартное значение Целочисленный положительный параметр precision определяет минимальное количество печатаемых цифр в записи целого числа (при использовании символов преобразования i, d, u, o, x или X ), количество цифр справа от десятичной точки при печати вещественных чисел (преобразование по форматам f, e, E, g или G ) или максимальное количество символов в строке (при выводе по формату s ). В случае отсутствия, этот параметр заменяется некоторым стандартным значением, зависящим от типа выводимого элемента данных Дополнительные спецификаторы h, l и L могут быть использованы для задания длины выводимого аргумента ( short или long ) Возвращаемое функцией fprintf значение равно количеству напечатанных символов
Пример использования: #include #define MAX 100 void main() { int i; float length[MAX], width[MAX]; FILE *printer; printer = fopen("prn", "w"); fprintf(printer, "\t\t\t*** Результаты расчета ***\n\n"); for (i = 0; i < MAX; i++) fprintf(printer, "%3d length = %6.3f, width = %6.3f\n", length[i], width[i]); }
Имя функции и назначение: fseek - перемещает указатель текущей позиции файла Формат и описание аргументов: int fseek(stream, offset, origin) FILE *stream; /* Указатель на открытый файл */ long offset; /* Смещение в байтах от origin */ int origin; /* Начальная позиция указателя */ Эта функция перемещает указатель, связанный с открытым файлом, на offset байт вперед или назад относительного заданного параметром origin начала отсчета. Последний может быть одной из следующих символических констант, определенных в файле stdio.h : SEEK_SET - начало файла SEEK_CUR - текущая позиция указателя SEEK_END - конец файла
Заметим, что допустимым является позиционирование указателя за пределами конца файла, однако его перемещение за начало файла приводит к ошибке. Возвращаемое значение равно нулю при нормальном завершении операции и отлично от нуля в противном случае Пример использования: #include void main() { int result; FILE *stream; stream = fopen("data", "r"); result = fseek(stream, 0L, SEEK_SET); }
Имя функции и назначение: rewind - устанавливает указатель текущей позиции на начало файла Формат и описание аргументов: void rewind(stream) FILE *stream; /* Указатель на открытый файл */ Эта функция не возвращает никакого значения в точку вызова
Пример использования: #include void main() { int dat_1 = 1, dat_2 = 7, dat_3, dat_4; FILE *stream; stream = fopen("data", "w+"); /*Запись данных в файл */ fprintf(stream, "%d %d", dat_1, dat_2); /* Установка указателя на начало */ rewind(stream); fscanf(stream, "%d %d", &dat_3, &dat_4); }
2. Операции ввода/вывода низкого уровня Функции ввода/вывода низкого уровня осуществляют обмен с файлами или периферийными устройствами путем прямого обращения к соответствующим функциям операционной системы (системным вызовам) Они не предоставляют возможности буферизации информации при пересылке и не обеспечивают преобразования данных из внутреннего машинного представления в текстовый формат
Перед обращением к существующему файлу его необходимо предварительно открыть при помощи функции open для ввода, вывода или выполнения обеих этих операций в текстовом или двоичном режиме Новый файл может быть создан путем использования функции creat. Обе эти функции возвращают в точку вызова целочисленный параметр (file handle), который следует использовать для ссылки на открытый файл во всех последующих операциях ввода/вывода низкого уровня или при управлении указателем текущей позиции После завершения работы с файлом, его нужно закрыть путем вызова функции close. В противном случае это сделает операционная система по окончании работа программы
При начале работы 5 file handle уже автоматически открыты stdin 0 stdout 1 stderr 2 stdaux 3 stdprn 4 Операции чтения и записи низкого уровня выполняются функциями read и write соответственно начиная с текущей позиции указателя Используя функцию lseek, указатель можно установить в произвольную позицию файла перед выполнением очередной операции ввода/вывода Проверка выхода за конец файла осуществляется функцией eof
Предварительные описания функций ввода/вывода низкого уровня помещены в файл io.h. Кроме этого, файлы fcntl.h, sys\types.h и sys\stat.h содержат определения символических констант, используемых отдельными функциями Для нормальной работы программы все эти файлы необходимо включить в ее исходный текст при помощи директивы препроцессора #include open - открыть файл close - закрыть файл create - создать файл eof - проверка на конец файла lseek - переместить указатель файла в заданное положение read - читать данные из файла write - записать данные в файл tell - получить текущее положение указателя файла
Имя функции и назначение: int open(pathname,oflag,[,pmode]) Формат и описание аргументов: int open(pathname,oflag,[,pmode]); char *pathname; /* Имя файла */ int oflag; int pmode;
Значения oflag : O_APPEND - присоединить к концу файла O_CREATE - создать и открыть новый файл для записи (если файл существует ничего не делать) O_EXCL - используется только с O_CREATE, чтобы вернуть значение ошибки если имя файла уже существует O_RDONLY - открыть файл только для чтения O_RDWR - открыть файл как для чтения, так и для записи O_TRUNC - O_WRONLY - открыть файл, только для записи O_TEXT - открыть в текстовом виде O_BINARY - открыть файл в двоичном виде Значения pmode : (используется только с O_CREATE, если файл уже существует pmode игнорируется) S_IWRITE S_IREAD S_IWRITE: S_IREAD Возвращает handle для открытых файлов, или 1, если ошибка
Пример использования: #include void main() { int ih; ih = open(info.dat,O_RDONLY); }
Имя функции и назначение: int read(handle,buffer,count) Функция пытается прочитать count байтов из файла, связанного с handle из buffer. Операция чтения начинается с текущей позиции указателя файлов Формат и описание аргументов: int read(handle,buffer,count); int handle; char *buffer; unsigned int count; Возвращает число действительно прочитанных символов, 0 - если была попытка прочитать конец файла, -1 – ошибка
Пример использования: #include void main() { int fh; unsigned int nbytes = 60000, bytesread; if ((fh = open(c:\data\conf.dat,O_RDONLY)) == -1) { perror(попытка открыть входной файл неудачна); exit(1); } if ((bytesread = read(fh,buffer,nbytes)) = = -1) perror( ); else printf(прочитано %d байтов из файла \n,bytesread);... close(fh); }
Имя функции и назначение: int close(handle); Функция закрывает файл, связанный с handle Возвращает: 0 - если файл успешно закрыт -1 если handle неправильный
3. Управление файлами и каталогами При разработке отдельных прикладных программ и тем более системного программного обеспечения могут оказаться полезными предоставляемые операционной системой дополнительные возможности управления структурой каталогов и изменения имен существующих файлов Соответствующие операции реализуются специальными функциями, входящими в состав библиотеки языка Си, что позволяет избежать непосредственного обращения к системным функциям ОС В этом разделе приведены краткие сведения о некоторых функциях рассматриваемой группы. Их предварительные описания помещены в стандартные файлы stdio.h и direct.h, которые при необходимости следует включить в исходный текст программы, используя директиву препроцессора #include
Имя функции и назначение: remove - удаляет файл с заданным именем Формат и описание аргументов: int remove(pathname) const char *pathname; /* Полное имя удаляемого файла */ Возвращаемое значение равно нулю при нормальном завершении операции и -1 в случае отсутствия в каталоге файла с заданным именем или запрете на его удаление (read-only file)
Пример использования: #include void main() { int result; result = remove("c:\\myfile"); if (result == -1) printf("Ошибка при удалении файла"); }
Имя функции и назначение: rename - изменяет имя существующего файла или каталога Формат и описание аргументов: int rename(oldname, newname) const char *oldname; /* Старое имя файла */ const char *newname; /* Новое имя файла */ Эта функция, переименовывающая существующие файлы и каталоги, позволяет также перемещать отдельные файлы из одного каталога в другой путем изменения их полных имен. В то же время, операции перемещения файлов между различными устройствами и целых каталогов не являются допустимыми Возвращаемое значение равно нулю при нормальном завершении операции и отлично от нуля в случае возникновения ошибки (неверное задание имени файла oldname, существование файла с именем newname или отсутствие возможности создать файл с таким именем, попытка перемещения отдельных файлов с одного устройства на другое и т. д.)
Пример использования: #include void main() { int result; result = rename("c:\\programs\myprog", "c:\\myprog"); }
Имя функции и назначение: chdir - изменяет текущий каталог Формат и описание аргументов: int chdir(pathname) char *pathname; /* Имя нового текущего каталога */ Возвращаемое значение равно нулю при нормальном изменении текущего каталога и -1 в случае возникновения ошибки Пример использования: #include void main() { chdir("..\\.."); }
Имя функции и назначение: mkdir - создает новый каталог Формат и описание аргументов: int mkdir(pathname) char *pathname; /* Имя создаваемого каталога */ Возвращаемое значение равно нулю при нормальном завершении операции и -1 в противном случае. Пример использования: #include void main() { int result; result = mkdir("b:\\newdir"); }
Имя функции и назначение: rmdir - удаляет существующий каталог Формат и описание аргументов: int rmdir(pathname) char *pathname; /* Имя удаляемого каталога */ Удаляемый этой функцией каталог должен быть пустым и не может являться текущим или корневым каталогом. Возвращаемое значение равно нулю при нормальном завершении операции и -1 в случае возникновения ошибки.
Внешние устройства как специальные файлы. Организация обмена со стандартными внешними устройствами В операционной среде всевозможные внешние устройства последовательного доступа (терминалы, устройства печати, графопостроители и т. д.) рассматриваются как специальные файлы Это обстоятельство играет важную роль с точки зрения гибкого и эффективного управления этими устройствами и общей технологии организации ввода/вывода Действительно, для того, чтобы передать данные на какое-либо внешнее устройство, достаточно записать их в некоторый стандартный файл, используя, например, обычные функции ввода/вывода потоком символов. При этом нет необходимости знать в деталях технические характеристики этого устройства, ибо все они известны операционной системе, в состав которой входят соответствующие программные драйверы Связь имен специальных файлов с конкретными внешними устройствами обычно устанавливается на уровне базовой системы ввода/вывода (BIOS)
Запуская в работу какую-либо программу, операционная система автоматически открывает пять стандартных файлов, ориентированных на выполнение операций ввода/вывода потоком. Эти файлы имеют предопределенные имена и характерные режимы доступа, приведенные в следующей таблице Имя файлаУстройствоРежим доступа stdin стандартное устройство ввода (обычно клавиатура терминала) чтение stdout стандартное устройство вывода (обычно экран терминала) запись stderr стандартное устройство для сообщений об ошибках (обычно экран терминала) запись stdaux стандартное вспомогательное устройство (обычно устройство, подключенное по последовательному интерфейсу COM1) чтение/запись stdprn стандартное устройство печатизапись
Предопределенные имена специальных файлов по существу являются указателями на структуру FILE и их допустимо использовать на месте параметра stream во всех функциях ввода/вывода потоком. Так, например, для печати сообщения на экране терминала достаточно воспользоваться функцией fprintf, задав в качестве ее первого параметра имя stdout : fprintf(stdout, "Это стандартное устройство вывода"); С другой стороны, ввод символа, поступившего от клавиатуры терминала, можно выполнить при помощи функции fgetc, адресовав ее к стандартному устройству ввода stdin : sym = fgetc(stdin);
Заметим, однако, что не все операции, определенные для обычных файлов, имеют смысл для стандартных устройств ввода/вывода Например, следующее обращение к функции fscanf fscanf(stdout, "%d", &intval); не является допустимым, ибо стандартное устройство вывода не может быть использовано в качестве устройства ввода Ошибочным также будет обращение к функции fseek при любом указатели на специальный файл
Для удобства работы со стандартными устройствами stdin и stdout в состав библиотеки языка Си дополнительно включены функции, специально ориентированные на обмен с этими устройствами Их имена во многом схожи с именами рассмотренных функций ввода/вывода потоком и приведены в следующей таблице Каждая из упомянутых здесь функций семантически подобна соответствующей ей общей функции и имеет те же самые аргументы, исключая указатель на структуру FILE. Их предварительные описания также помещены в файл stdio.h (кроме описания функции getch(), которое находится в файле conio.h ).
Имя функцииНазначениеЭквивалентное обращение getch() читает очередной символ от клавиатуры терминала без эха на экране нет fgetchar() getchar() читают очередной символ со стандартного устройства ввода fgetc(stdin) fputchar(s) putchar(s) записывают символ s на стандартное устройство вывода fputc(s, stdout) gets(str) читает строку символов str со стандартного устройства ввода fgets(str, 256, stdin) puts(str) копирует строку символов str на стандартное устройство вывода puts(str, stdout) scanf(...) читает форматированные данные со стандартного устройства ввода fscanf(stdin,...) printf(...) записывает форматированные данные на стандартное устройство вывода fprintf(stdout,...)
Операции ввода/вывода через порты Все периферийные устройства и внешняя память подключены к системной шине компьютера через специальные интерфейсы Каждый такой интерфейс имеет набор встроенных регистров, называемых портами ввода/вывода, через которые центральный процессор и память взаимодействуют с соответствующими внешними устройствами. Одни порты предназначены для буферирования данных и называются буферными портами или портами данных Другие служат для хранения информации о состоянии устройства или интерфейса и носят название портов состояния Третьи, называемые портами управления, используются для получения команд от центрального процессора
Стандартная библиотека компилятора предоставляет пользователям две функции с именами inp() и outp(), осуществляющие прямой обмен информацией с портами микропроцессора Возможность выполнения операций ввода/вывода столь низкого уровня необходима при разработке программных драйверов устройств, однако она может оказаться полезной и при подготовке прикладных программ, требующих взаимодействия с периферийным оборудованием в реальном масштабе времени Предварительные описания функций inp() и outp() помещены в файл conio.h
Имя функции и назначение: inp - читает один информационный байт через входной порт с заданным номером Формат и описание аргументов: int inp(port) unsigned port ; /* Номер опрашиваемого порта */ Пример использования: #include void main() { char result; unsigned port = 0x64; result = inp(port); printf("Содержимое порта 64H равно %0x\n", (int)result); }
Имя функции и назначение: outp - записывает один информационный байт в выходной порт с заданным номером Формат и описание аргументов: int outp(port, value) unsigned port; /* Номер выходного порта */ int value; /* Выводимое значение */ Возвращаемое функцией outp значение равно параметру value Пример использования: #include void main() { unsigned port = 0x64; outp(port, 0x03); }
Имя функции и назначение: cputs(str) - записывает строку (записанную вместе с символом конца строки) на консоль Формат и описание аргументов: void cputs(str); char *str; /* Выводимая строка */ Пример использования: #include void main() { char *buffer = " Вставьте диск с данными в драйвер A:\R\N " ); cputs(buffer); }
Имя функции и назначение: kbhit() - проверяет консоль на нажатие клавиши Возвращает ненулевое значение, если клавиша была нажата, 0 иначе Формат и описание аргументов: int kbhit()
Пример использования: #include void main() { int result; result = kbhit(); } Если результат ненулевой, введенный символ находится в буфере. Он может быть извлечен getch или getc Если getch или getc были вызваны без проверки посредством kbhit, программа перейдет в ожидание нажатия клавиши