|
Средства ввода-вывода в Си++
Средства ввода-вывода в Си++
2 - Введение. 2
- Общие положения. 3
- Потоковый ввод-вывод 9
- Форматный ввод-вывод. 13
- Форматный ввод из входного потока. 15
- Литература 17
- Введение.
- В стандарте языка Си отсутствуют средства ввода-вывода. Все операции ввода-вывода реализуются с помощью функций, находящихся в библиотеке языка Си, поставляемой в составе конкретной системы программирования Си. Во время работы с файлами данные могут передаваться или в своем внутреннем двоичном представлении или в текстовом формате, то есть в более удобочитаемом виде.
- Особенностью языка Си, который впервые был применен три разработке операционной системы UNIX, является отсутствие заранее спланированных структур файлов. Все файлы рассматриваются как неструктурированная последовательность байтов. При таком подходе к организации файлов удалось распространить понятие файла и на различные устройства. В UNIX конкретному устройству соответствует так называемый "специальный файл", а одни и те же функции библиотеки языка Си используются как для обмена данными с файлами, так и для обмена с устройствами.
- Библиотека языка Си поддерживает три уровня ввода-вывода: потоковый ввод-вывод, ввод-вывод нижнего уровня и ввод-вывод для консоли и портов. Последний уровень, обеспечивающий удобный специализированный обмен данными с дисплеем и портами ввода-вывода, мы рассматривать не будем в силу его системной зависимости. Например, он различен для MS-DOS, Windows и UNIX.
- Общие положения.
- Язык Си является фундаментом С++. При этом С++ поддерживает всю файловую систему Си. Поэтому при использовании С-кода в С++ нет необходимости менять процедуры ввода-вывода. Хотя при написании программ на С++ обычно более удобно использовать именно систему С++. Это касается, в частности, и использования "iostream.h" взамен "stdio.h", реализующим ввод-вывод. Изучим файловый ввод-вывод в языке Си. Тем более, что это само по себе очень интересно и очень важно для понимания "потоков" и "файлов" как в Си, так и в С++.
- В системе ввода-вывода в Си для программ поддерживается единый интерфейс, не зависящий от того, к какому конкретному устройству осуществляется доступ. То есть в Си между программой и устройством находится нечто более общее, чем само устройство. Такое обобщенное устройство ввода или вывода (устройство более высокого уровня абстракции) называется потоком. В то же время конкретное устройство называется файлом. Наша задача - понять, каким обрзом происходит взаимодействие потоков и файлов.
- Файловая система Си предназначена для работы с разными устройствами, в том числе с терминалами, дисководами и накопителями. Даже, если какое-то устройство очень сильно отличается от других устройств, буферизованная файловая система все равно представит его в виде логического устройства, которое называется потоком. Все потоки ведут себя похожим образом. И так как они в основном не зависят от физических устройств, то та же функция, которая выполняет запись в дисковый файл, может ту же операцию выполнить и на другом устройстве. Например, на консоли. Потоки бывают двух видов: текстовые и двоичные.
- В языке Си файлом может быть все, что угодно, начиная в дискового файла и заканчивая терминалом или принтером. Поток связывают с определенным файлом, выполняя обязательную операцию открытия. Как только файл открыт, можно проводить обмен информацией между ним и программой.
- Но не у всех файлов одинаковые возможности. Например, к дисковому файлу прямой доступ возможен, в то время как к некоторым принтерам - он не возможен. Таким образом, вы видите, что напрашивается определенный вывод, являющийся принципом системы ввода-вывода языка Си: все потоки одинаковы, а файлы - нет!
- Если файл может поддерживать запросы на местоположение (указатель текущей позиции), то при открытии такого файла указатель текущей позиции в файле устанавливается в начало файла. При чтении каждого символа из файла (или записи в файл) указатель текущей позиции увеличивается. Тем самым обеспечивается продвижение по файлу.
- Файл отсоединяется от определенного потока (то есть разрывается связь между файлом и потоком) с помощью операции закрытия файла. При закрытии файла, открытого с целью вывода, содержимое (если оно, конечно, есть) связанного с ним потока записывается на внешнее устройство. Этот процесс обычно называют дозаписью потока. При этом гарантируется, что никакая информация случайно не останется в буфере диска.
- Если программа завершает работу нормально, то есть либо main() возвращает управление операционной системе, либо выход происходит через exit(), то все файлы закрываются автоматически.
- В случае же аварийного завершения работы программы, например, в случа краха или завершения путем вызова abort(), файлы не закрываются.
- У каждого потока, связанного с файлом, имеется управляющая структура, содержащая информацию о файле. Она имеет тип FILE. Блок управления файлом - это небольшой блок памяти, временно выделенный операционной системой для хранения информации о файле, который был открыт для использования. Блок управления файлом обычно содержит информацию об идентификаторе файла, его расположении на диске и указателе текущей позиции в файле.
- Для выполнения всех операций ввода-вывода следует использовать только понятия потоков и применять всего лишь одну файловую систему. Ввод или вывод от каждого устройства автоматически преобразуется системой в легко управлемый поток. И это является достижением языка Си.
- Таковы основополагающие замечания относительно существования различных потоков информации и связанных с ними файлов.
- Файловая система языка Си состоит из нескольких взаимосвязанных между собой функций. Для их работы в Си требуется заголовочный файл <stdio.h> и такой же аналогичный ему заголовочный файл <iostream.h> требуется для работы в С++.
- Ниже приведена таблица основных (часто используемых) функций файловой системы языка Си.
|
Имя | Что делает эта функция | Имя | Что делает эта функция | | fopen() | Открывает файл | feof() | Возвращает значение true (истина), если достигнут конец файла | | fclose() | Закрывает файл | ferror() | Возвращает значение true (истина), если произошла ошибка | | putc() | Записывает символ в файл | remove() | | | fputc() | То же, что и putc() | fflush() | Дозапись потока в файл | | getc() | Читает символ из файла | rewind() | Устанавливает указатель текущей позиции в начало файла | | fgetc() | То же, что и getc() | ftell() | Возвращает текущее значение указателя текущей позиции в файле | | fgets() | Читает строку из файла | fprintf() | Для файла то же, что printf() для консоли | | fputs() | Записывает строку в файл | fscanf() | Для файла то же, что scanf() для консоли | | fseek() | Устанавливает указатель текущей позиции на определенный байт файла | | | | |
- Заголовок <stdio.h> представляет прототипы функций ввода-вывода в Си и определяет следующие три типа: size_t, fpos_t и FILE. Первые два: size_t, fpos_t представляют собой разновидности такого типа, как целое без знака. Отдельно рассмотрим третий тип: FILE.
- Указатель файла - это то, что соединяет в единое целое всю систему ввода-вывода языка Си. Указатель файла - это указатель на структуру типа FILE. Он указывает на структуру, содержащую различные сведения о файле, например, его имя, статус, и указатель текущей позиции в начало файла. В сущности указатель файла определяет конкретный файл и используется соответствующим потоком при выполнении функции ввода-вывода.
- Чтобы выполнять в файлах операции чтения и записи, программы должны использовать указатели соответствующих файлов. Чтобы объвить переменную-указатель файла необходимо использовать следующий оператор:
- FILE *fp;
- Функция fopen() открывает поток и связывает с этим потоком файл. Затем она возвращает указатель этого файла. Прототип функции имеет вид:
- FILE *fopen(const char *имя_файла, const char *режим);
- Здесь имя_файла - это указатель на строку символов, представляющую собой допустимое имя файла, в которое может входить спецификация файла (включает обозначение логического устройства, путь к файлу и собственно имя файла).
- Режим - определяет, каким образом файл будет открыт. Ниже в таблице показаны допустимые значения режимов.
|
Режим | Что обозначает данный режим | | r | Открыть текстовый файл для чтения | | w | Создать текстовый файл для записи | | a | Добавить в конец текстового файла | | wb | Создать двоичный файл для записи | | rb | Открыть двоичный файл для чтения | | ab | Добавить в конец двоичного файла | | r+ | Открыть текстовый файл для чтения/записи | | w+ | Создать текстовый файл для чтения/записи | | a+ | Добавить в конец текстового файла или создать текстовый файл для чтения/записи | | r+b | Открыть двоичный файл для чтения/записи | | w+b | Создать двоичный файл для чтения/записи | | a+b | Добавить в конец двоичного файла или создать двоичный файл для чтения/записи | | |
- Приведем фрагмент программы, в котором используется функция fopen() для открытия файла по имени TEST.
- FILE *fp;
- fp = fopen("test", "w");
- Следует сразу же указать на недостаточность такого кода в программе. Хотя приведенный код технически правильный, но его обычно пишут немного по-другому.
- FILE *fp;
- if ((fp = fopen("test", "w")==NUL)
- {
- printf("Ошибка при открытии файла.\n\r")"
- exit(1);
- }
- Рис. 1
- Этот метод помогает при открытии файла обнаружить любую ошибку.
- Например, защиту от записи или полный диск. Причем, обнаружить еще до того, как программа попытается в этот файл что-то записать. Поэтому всегда нужно вначале получить подтверждение, что функция fopen() выполнилась успешно, и лишь затем выполнять c файлом другие операции. Ниже на рисунке 1 приведена небольшую часть программы, которая. подтверждает или не подтверждает открытие файла. Результаты работы указанной программы приведены на рисунке 2.
- Рис. 2.
- Потоковый ввод-вывод
- На уровне потокового ввода-вывода обмен данными производится побайтно. Такой ввод-вывод возможен как для собственно устройств побайтового обмена (печатающее устройство, дисплей), так и для файлов на диске, хотя устройства внешней памяти, строго говоря, являются устройствами поблочного обмена, т.е. за одно обращение к устройству производится считывание или запись фиксированной порции данных. Чаще всего минимальной порцией данных, участвующей в обмене с внешней памятью, являются блоки в 512 байт или 1024 байта. При вводе с диска (при чтении из файла) данные помещаются в буфер операционной системы, а затем побайтно или определенными порциями передаются программе пользователя. При выводе данных в файл они накапливаются в буфере, а при заполнении буфера записываются в виде единого блока на диск за одно обращение к последнему. Буферы операционной системы реализуются в виде участков основной памяти. Поэтому пересылки между буферами ввода-вывода и выполняемой программой происходят достаточно быстро в отличие от реальных обменов с физическими устройствами.
- Функции библиотеки ввода-вывода языка Си, поддерживающие обмен данными с файлами на уровне потока, позволяют обрабатывать данные различных размеров и форматов, обеспечивая при этом буферизованный ввод и вывод. Таким образом, поток - это файл вместе с предоставляемыми средствами буферизации.
- При работе с потоком можно производить следующие действия:
- · открывать и закрывать потоки (связывать указатели на потоки с конкретными файлами);
- · вводить и выводить: символ, строку, форматированные данные, порцию данных произвольной длины;
- · анализировать ошибки потокового ввода-вывода и условие достижения конца потока (конца файла);
- · управлять буферизацией потока и размером буфера;
- · получать и устанавливать указатель (индикатор) текущей позиции
- При открытии потока могут возникнуть следующие ошибки: указанный файл, связанный с потоком, не найден (для режима "чтение"); диск заполнен или диск защищен от записи и т.п. Необходимо также отметить, что при выполнении функции fopen() происходит выделение динамической памяти. При её отсутствии устанавливается признак ошибки "Not enough memory" (недостаточно памяти). В перечисленных случаях указатель на поток приобретает значение NULL. Заметим, что указатель на поток в любом режиме, отличном от аварийного никогда не бывает равным NULL.
- Приведем типичную последовательность операторов, которая используется при открытии файла, связанного с потоком:
- if ((fp = fopen("t.txt","w")) == NULL)
- perror("ошибка при открытии файла t.txt \n");
- exit(0);
- }
- Где NULL - нулевой указатель, определенный в файле stdio.h.
- Открытые на диске файлы после окончания работы с ними рекомендуется закрыть явно. Для этого используется библиотечная функция
- int fclose (указатель_на_поток);
- Открытый файл можно открыть повторно (например, для изменения режима работы с ним) только после того, как файл будет закрыт с помощью функции fclose().
- Когда программа начинает выполняться, автоматически открываются пять потоков, из которых основными являются:
- · стандартный поток ввода (на него ссылаются, используя предопределенный указатель на поток stdin);
- · стандартный поток вывода (stdout);
- · стандартный поток вывода сообщений об ошибках (stderr).
- По умолчанию стандартному потоку ввода stdin ставится в соответствие клавиатура, а потокам stdout и stderr соответствует экран дисплея.
- Одним из наиболее эффективных способов осуществления ввода-вывода одного символа является использование библиотечных функций getchar( ) и putchar(). Прототипы этих функций имеют следующий вид:
- int getchar(void);
- int putchar(int c);
- Функция getchаr( ) осуществляет ввод одного символа. При обращении она возвращает в вызвавшую ее функцию один введенный символ.
- Функция putchar( ) выводит в стандартный поток один символ, при этом также возвращает в вызвавшую ее функцию только что выведенный символ.
- Обратите внимание на то, что функция getchar( ) вводит очередной байт информации (символ) в виде значения типа int. Это сделано для того, чтобы гарантировать успешность распознавания ситуации "достигнут конец файла". Дело в том, что при чтении из файла с помощью функции getchar() может быть достигнут конец файла. В этом случае операционная система в ответ на попытку чтения символа передает функции getchar() значение EOF (End of File). Константа EOF определена в заголовочном файле stdio.h и в разных операционных системах имеет значение 0 или -1. Таким образом, функция getchar() должна иметь возможность прочитать из входного потока не только символ, но и целое значение. Именно с этой целью функция getchar( ) всегда возвращает значение типа int.
- В случае ошибки при вводе функция getchar() также возвращает EOF.
- При наборе текста на клавиатуре коды символов записываются во внутренний буфер операционной системы, Одновременно они отображаются (для визуального контроля) на экране дисплея. Набранные на клавиатуре символы можно редактировать (удалять и набирать новые). Фактический перенос символов из внутреннего буфера в программу происходит при нажатии клавиши . При этом код клавиши также заносится во внутренний буфер. Таким образом, при нажатии на (Клавишу 'А' и клавишу (завершение ввода) во внутреннем буфере оказываются: код символа 'А' и код клавиши . ) Об этом необходимо помнить, если вы рассчитываете на ввод функцией getchar() одиночного символа.
- Приведём в пример программу копирования из стандартного ввода в стандартный вывод:
- #include
- int main()
- {
- int c;
- while ((c=getchar())!=EOF)
- Putchar(c);
- return 0;
- }
- Для завершения приведенной выше программы копирования необходимо ввести с клавиатуры сигнал прерывания Ctrl+C.
- Одной из наиболее популярных операций ввода-вывода является операция ввода-вывода строки символов. В библиотеку языка Си для обмена данными через Стандартные потоки ввода-вывода включены функции ввода-вывода строк gets() и puts(), которые удобно использовать при создании диалоговых систем. Прототипы этих функций имеют следующий вид:
- char * gets (char * s); /* Функция ввода */
- int puts (char * s); /* Функция вывода */
- Обе функции имеют только один аргумент - указатель s на массив символов. Бели строка прочитана удачно, функция gets( ) возвращает адрес того массива s, в который производился ввод строки. Если произошла ошибка, то возвращается NULL.
- Функция puts() в случае успешного завершения возвращает последний выведенный символ, который всегда является символом `\n'. Если произошла ошибка, то возвращается EOF.
- Форматный ввод-вывод.
- Для работы со стандартными потоками в режиме форматного ввода-вывода определены две функции:
- printf( ) - форматный вывод;
- scanf( ) - форматный ввод.
- Прототип функции printf() имеет вид:
- int printf(const char *format,...);
- При обращении к функции printf() возможны две формы задания первого параметра:
- int printf ( *форматная строка, список_аргументов);
- int printf (указателъ_на_форматную_строку,. список_аргументов);
- В обоих случаях функция printf() преобразует данные из внутреннего представления в символьный вид в соответствии с форматной строкой и выводит их в выходной поток. Данные, которые преобразуются и выводятся, задаются как аргументы функции printf().
- Возвращаемое значение функции printf() - число напечатанных символов; а в случае ошибки - отрицательное число.
- Форматная_строка ограничена двойными кавычками и может включать произвольный текст, управляющие символы и спецификации преобразования данных. Текст и управляющие символы из форматной строки просто копируются в выходной поток. Форматная строка обычно размещается в списке фактических параметров функции, что соответствует первому варианту вызова функции printf(). Второй вариант предполагает, что первый фактический параметр - это указатель типа char *, a сама форматная строка определена в программе как обычная строковая константа или переменная.
- В список аргументов функции printf() включают выражения, значения которых должны быть выведены из программы. Частные случаи этих выражений - переменные и константы. Количество аргументов и их типы должны соответствовать последовательности спецификаций преобразования в форматной строке. Для каждого аргумента должна быть указана точно одна спецификация преобразования.
- Если аргументов недостаточно для данной форматной строки, то результат зависит от реализации (от операционной системы и от системы программирования). Если аргументов больше, чем указано в форматной строке, "лишние" аргументы игнорируются. Гарантируется, что при любом количестве параметров и любом их типе после выполнения функций printf() дальнейшее выполнение программы будет корректным.
- Спецификация преобразования имеет следующую форму:
- % флаги ширина_поля. точностъ спецификатор
- Символ % является признаком спецификации преобразования. В спецификации преобразования обязательными являются только два элемента: признак % и спецификатор.
- Спецификатор Тип аргумента Формат вывода
- d int, char,unsigned Десятичное целое со знаком
- u int, char,unsigned Десятичное целое без знака
- o int, char,unsigned Восьмеричное целое без знака
- x int, char,unsigned Шестнадцатеричное целое без знака; при выводе используются символы “0..9a..f”
- X int, char,unsigned Шестнадцатеричное целое без знака; при выводе используются символы "0...9A...F”
- f double, float Вещественное значение со знаком в виде:
- Знак_числа dddd.dddd
- где dddd - одна или более десятичных цифр. Количество цифр перед десятичной точкой зависит от величины выводимого числа, а количество цифр после десятичной точки зависит от требуемой точности. Знак числа при отсутствии модификатора '+' изображается только для отрицательного числа.
- Форматный ввод из входного потока.
- Форматный ввод из входного потока осуществляется функцией scanf(). Прототип функции scanf( ) имеет вид:
- int scanf(const char * format, …);
- При обращении к функции scanf() возможны две формы задания первого параметра:
- int scanf ( форматная строка, список аргументов );
- int scanf(указатель_на_форматную _строку, список_аргументов);
- Функция scanf() читает последовательности кодов символов (байты) из входного потока и интерпретирует их в соответствии с форматной_строкой как целые числа, вещественные числа, одиночные символы, строки. В первом варианте вызова функции форматная строка размещается непосредственно в списке фактических параметров. Во втором варианте вызова предполагается, что первый фактический параметр - это указатель типа char *, адресующий собственно форматную строку. Форматная строка в этом случае должна быть определена в программе как обычная строковая константа или переменная.
- После преобразования во внутреннее представление данные записываются в области памяти, определенные аргументами, которые следуют за форматной строкой. Каждый аргумент должен быть указателем на переменную, в которую будет записано очередное значение данных и тип которой соответствует типу, указанному в спецификации преобразования из форматной строки.
- Если аргументов недостаточно для данной форматной строки, то результат зависит от реализации (от операционной системы и от системы программирования). Если аргументов больше, чем требуется в форматной строке, "лишние" аргументы игнорируются.
- Последовательность кодов символов, которую функция scanf() читает, из входного потока, как правило, состоит из полей (строк), разделенных символами промежутка или обобщенными пробельными символами. Поля просматриваются и вводятся функцией scanf() посимвольно. Ввод поля прекращается, если встретился пробельный символ или в спецификации преобразования точно указано количество вводимых символа.
- Функция scanf() завершает работу, если исчерпана форматная строка. При успешном завершении scanf() возвращает количество преобразованных и введенных полей (точнее, количество объектов, получивших значения при вводе). Значение EOF возвращается при возникновении ситуации "конец файла"; значение -1 - при возникновении ошибки преобразования данных.
- Рассмотрим форматную строку функции scanf(): "code: %d %*s %c %s"
- Строка "code:" присутствует во входном потоке для контроля вводимых данных и поэтому указана в форматной строке. Спецификации преобразования задают следующие действия:
- %d - ввод десятичного целого;
- %*s - пропуск строки;
- %с - ввод одиночного символа;
- %s - ввод строки.
- Приведем результаты работы программы для трех различных наборов входных данных.
- 1.Последовательность символов исходных данных:
- code: 5 поле2 D asd
- Результат выполнения программы:
- i=5 c=D s=asd ret=3
- Значением переменной ret является код возврата функц»-scanf(). Число 3 говорит о том, что функция scanf() ввела данные без ошибки и было обработано 3 входных поля (строки "code:" и "поле2" пропускаются при вводе).
Заключение.В данной работе были рассмотрены особенности операций ввода-вывода в языке програмирования Си/С++, в которых есть много общего. Рассмотрены понятия «функции» и «потока», их виды и особенности функционирования в языке программирования.Боллее глубоко и подробно были рассмотрены потоковый ввод-вывод символов, его особенности работы. В работе для иллюстрации приведены фрагменты программ с кодом, которые показывают особенности реализации тех или иных функций ввода-вывода.Литература1. Подбельский В.В. Программирование на языке С. М.: Финансы и статистика, 1999. 2. В.В.Подбельский, С.С.Фомин «Язык Си++». - М.: Финансы и статистика, 2003.3. Павловская Т.А., Щупак Ю.А. С/С++. Структур-ное программирование (практикум). - Спб.: Пи-тер, 2004. 4. Аляев Ю.А., Козлов О.А. Программирование. Pascal, C++, Visual Basic. М.: Финансы и стати-стика, 2004. 5. Гуденко Д., Петроченко Д. Сборник задач по программированию. - Спб.: Питер, 2003. 6. Культин Н. С/С++ в задачах и примерах.. БХВ - Петербург, 2004. 7. Фридман А., Кландер Л. И др. С/С++. Алгоритмы и примеры. М. Бином, 2003.
|
|