Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции КПиЯП.docx
Скачиваний:
50
Добавлен:
20.09.2019
Размер:
3.8 Mб
Скачать

Лекция 11. Файлы

Файл - именованная область данных на каком-либо носителе информации (жесткий диск, дискета, компакт-диск и т.д. и т.п.).

Язык С, как и другие языки программирования высокого уровня, позволяет осуществлять операции файлового ввода и вывода. Основной алгоритм обработки файлов выполняется в три действия в следующем порядке:

  • открытие файла;

  • чтение и/или запись данных в файл;

  • закрытие файла.

В языке С все файлы делятся на два вида:

  • бинарные;

  • текстовые.

Бинарный файл - файл, содержащий структурированную или не структурированную информацию, представленную в двоичном (бинарном) виде.

Текстовый файл - файл, содержащий структурированную или не структурированную информацию, представленную в текстовом (ASCII символы) виде.

Операции чтения и записи для текстовых и бинарных файлов отличаются друг от друга (в языке С реализованы в виде различных функций). В языке С для работы с файлами набор функций реализован в библиотеке stdio.h.

В языке С файл также называют файловым потоком ввода и/или вывода. Поток - системный объект, в который и/или из которого могут быть записаны и/или считаны данные. В любой программе на языке С автоматически доступны следующие потоки:

  • stdin - стандартный поток ввода (обычно ассоциирован с клавиатурой);

  • stdout - стандартный поток вывода (обычно ассоциирована с экраном);

stderr - стандартный поток ошибок (обычно ассоциирована с экраном).

Переменная для работы с файлами описывается в виде указателя на тип FILE:

FILE *имя [= NULL];

Функция открытия файла:

FILE * fopen(const char *filename, const char *mode);

Первый параметр filename - имя файла, который необходимо открыть. Имя файла может быть указано в виде:

  • только имя файла - если файл находится в текущем рабочем каталоге программы;

  • абсолютный (полный) путь к файлу;

  • относительный путь к файлу.

Второй параметр mode - режим открытия файла (строка). Перечень возможных режимов открытия файлов в языке С приведен в таблице 10.1. Функция возвращает указатель на файловую переменную, если удалось открыть указанный файл в заданном режиме. В противном случае (если файл открыть не удалось) функция возвращает значение NULL.

Функция переопределения файлового потока:

FILE * freopen(const char *filename, const char *mode,

FILE * restrict stream);

Первый параметр filename - имя файла, с которым необходимо ассоциировать существующую файловую переменную (параметр stream). Второй параметр mode - режим открытия нового файла (см. таблицу 10.1). Третий параметр stream - существующий файловый поток, который необходимо ассоциировать с новым файлом.

Таблица 10.1 - Режимы открытия файла

Функция закрывает файл, ассоциированный со stream, открывает файл, указанный в filename, и ассоциирует stream с только что открытым файлом. Функция возвращает указатель на перенаправленный файловый поток, или значение NULL - если произошла ошибка. Данная функция обычно используется для перенаправления потоков stdin, stdout, stderr.

Функция создания временного файла:

FILE * tmpfile(void);

Функция создает временный файл и возвращает указатель на файловую переменную. Файл создается в режиме «wb+». В случае ошибки функция возвращает значение NULL. Файл уничтожается после его закрытия или после завершения программы.

Функция закрытия файла:

int fclose(FILE *stream);

В качестве параметра stream передается указатель на открытый ранее файловый поток. Функция осуществляет закрытие файла и возвращает значение ноль, если закрытие успешно и значение EOF (end of file, целочисленное значение: -1) - если нет.

Функция проверки достижения конца файла:

int feof(FILE *stream);

В параметре stream передается указатель на файловый поток. Функция возвращает не нулевое значение, если достигнут конец файла и нулевое значение - если нет.

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

Функция форматированного ввода из файла:

int fscanf(FILE * restrict stream,

const char * restrict format,

[argument] ...);

В первом параметре stream передается указатель на файловый поток, из которого необходимо произвести чтение. Остальные параметры функции аналогичны параметрам функции scanf. Функция возвращает количество успешно считанных и расположенных значений. В случае возникновения ошибки или достижения конца файла функция возвращает значение EOF.

Функция форматированного вывода в файл:

int fprintf(FILE * restrict stream,

const char * restrict format,

[argument] ...);

В первом параметре stream передается указатель на файловый поток, в который необходимо вывести значения. Остальные параметры функции аналогичны параметрам функции printf. Функция возвращает количество выведенных символов в поток. В случае возникновения ошибки - отрицательное значение.

Функция чтения строки из файла:

char * fgets(char * restrict buffer, int maxlen,

FILE * restrict stream);

В первом параметре buffer передается указатель на буфер ввода (куда должна быть записана строка). Во втором параметре maxlen передается максимальное количество символов для считывания. В третьем параметре stream передается указатель на файловый поток, из которого необходимо произвести чтение. Функция осуществляет считывание строки в переменную buffer из потока stream до первого символа перевода строки (\n) или до конца файла. Если длина строки превышает указанное во втором параметре значение, то строка усекается. Символ перевода строки, если он присутствует, дописывается в конец считанной строки. Функция возвращает значение buffer или значение NULL, если произошла ошибка или была попытка чтения из конца файла.

Функция записи строки в файл:

int fputs(const char * restrict string,

FILE * restrict stream);

В первом параметре string передается строка, которую необходимо записать в файл. Во втором параметре stream передается указатель на файловый поток, в который необходимо вывести строку. Функция возвращает положительное значение при успешном выполнении, и значение EOF - при ошибке.

Функция чтения символа из файла:

int fgetc(FILE *stream);

В параметре stream передается указатель на файловый поток, из которого необходимо прочитать символ. Функция возвращает код считанного символа (положительное значение) или значение EOF - если произошла ошибка или была произведена попытка чтения из конца файла.

Функция записи символа в файл:

int fputc(int c, FILE *stream);

В первом параметре c передается код символа, который необходимо записать в файл. Во втором параметре stream передается указатель на файловый поток, в который осуществляется запись символа. Функция возвращает код записанного символа, или EOF, если произошла ошибка.

ПРИМЕЧАНИЕ: Здесь приведены только основные функции ввода и вывода в текстовые файлы. В библиотеке stdio.h содержатся и другие функции, схожие по назначению.

Далее приведены функции чтения и записи в бинарные файлы.

Функция чтения данных из бинарного файла:

size_t fread(void * restrict buffer, size_t size,

size_t num, FILE * restrict stream);

В первом параметре функции buffer передается указатель на буфер, куда должны быть записаны считанные из файла данные. Во втором параметре size передается размер (в байтах) одного значения. В третьем параметре num передается количество значений для считывания. Фактический объем данных, которые должны быть считаны, вычисляется путем перемножения второго и третьего параметров. В четвертом параметре stream передается указатель на файловый поток, из которого необходимо производить чтение данных. Функция считывает из файлового потока stream num значений, каждое из которых имеет размер size. Считанные значения располагаются последовательно в буфере buffer. Функция возвращает количество успешно считанных значений, которое может быть меньше чем num, если произошла ошибка или был достигнут конец файла.

Функция записи данных в бинарный файл:

size_t fwrite(const void * restrict buffer,

size_t size, size_t num,

FILE * restrict stream);

В первом параметре функции buffer передается указатель на буфер, откуда должны быть записаны данные в файл. Во втором параметре size передается размер (в байтах) одного значения. В третьем параметре num передается количество значений для записи. Фактический объем данных, которые должны быть записаны, вычисляется путем перемножения второго и третьего параметров. В четвертом параметре stream передается указатель на файловый поток, в который необходимо производить запись данных. Функция записывает из буфера buffer в файловый поток stream num значений, каждое из которых имеет размер size. Функция возвращает количество успешно записанных значений, которое может быть меньше чем num, если произошла ошибка.

Помимо функций чтения и записи данных в файл на практике часто используются функции навигации (перемещения) по файлу.

Функция перехода к началу файла:

void rewind(FILE *stream);

В параметре stream передается указатель на файловую переменную, к началу файла которой необходимо перейти. Функция устанавливает текущую позицию в файле на начало файла.

Функция установки позиции в файле:

int fseek(FILE *stream, long int offset, int origin);

Первый параметр stream - указатель на файловую переменную, позицию, в файле которой, необходимо изменить. Второй параметр offset - смещение (в байтах), на которое необходимо установить позицию в файле. Третий параметр origin - указывает, откуда отсчитывать смещение:

  • SEEK_SET (целочисленный код - 0) - от начала файла;

  • SEEK_CUR (целочисленный код - 1) - от текущей позиции в файле;

  • SEEK_END (целочисленный код - 2) - от конца файла.

Функция получения текущей позиции в файле:

long int ftell(FILE *stream);

В параметре stream передается указатель на файловую переменную, позицию в файле которой, необходимо получить. Функция возвращает текущее смещение в файле (в байтах) от начала файла. Если произошла ошибка, то значение -1.

Функция чтения текущей позиции в файле:

int fgetpos(FILE * restrict stream,

fpos_t * restrict pos);

Первый параметр stream - файловый поток, позицию которого в файле необходимо получить. Второй параметр pos - указатель на структуру fpos_t, куда будет помещена информация о позиции файлового потока в файле. Функция возвращает значение ноль при успешном выполнении и не нулевое значение при ошибке.

ПРИМЕЧАНИЕ: Структура fpos_t описана в библиотеке stdio.h.

Функция установки текущей позиции в файле:

int fsetpos(FILE *stream, const fpos_t *pos);

Первый параметр stream - файловый поток, позицию которого в файле необходимо установить. Второй параметр pos - указатель на структуру fpos_t, в которой содержится информация о новой позиции файлового потока в файле. Функция возвращает значение ноль при успешном выполнении и не нулевое значение при ошибке.

ПРИМЕЧАНИЕ: Функции fgetpos и fsetpos используются вместе. Сначала считывается текущая позиция в файле с помощью функции fgetpos, а затем, после проведения операций чтения и/или записи в файл, данная позиция может быть восстановлена с помощью функции fsetpos.

В языке С файловый поток может быть буферизированным или не буферизированным. Наличие буфера ввода означает, что поступающие данные помещаются сначала в буфер, а затем, по мере потребности, передаются в программы посредством функций ввода. Наличие буфера вывода означает, что записываемые в файл данные сначала помещаются в буфер, а затем, при заполнении буфера или при закрытии потока, записываются в файл. Отсутствие буферов означает, что ввод и вывод осуществляется напрямую в файл. Для управления буферами в библиотеке stdio.h языка С присутствует несколько функций.

Функция управления буфером:

void setbuf(FILE * restrict stream,

char * restrict buffer);

Первый параметр stream - файловый поток, управление буфером которого осуществляется. Второй параметр buffer - указатель на область буфера. Если значение параметра buffer - NULL, то поток будет не буферизированным. В противном случае параметр buffer должен содержать указатель на область памяти размером BUFSIZ (мнемоническая константа, определенная в библиотеке stdio.h, стандартное значение 512). Устанавливать буфер ввода или вывода можно только до проведения операций чтения или записи данных в файловый поток (лучше сразу после его открытия).

Функция очистки буфера:

int fflush(FILE *stream);

В параметре stream передается указатель на файловый поток, буфер которого необходимо очистить. Если это поток вывода, то все данные, находящиеся в буфере, записываются в файл. Если это поток ввода, то все данные, находящиеся в буфере, уничтожаются. Функция не влияет на не буферизированные потоки. Если в качестве параметра передается значение NULL, то осуществляется очистка буферов всех буферизированных потоков. Функция возвращает значение ноль при успешном выполнении, в противном случае - значение EOF.

Также в библиотеке stdio.h реализованы две функции для работы с файлами на «верхнем» уровне (не используя файловый поток).

Функция удаления файла:

int remove(const char *filename);

В параметре filename указывается имя существующего файла, который необходимо удалить. Функция возвращает нулевое значение, если файл был успешно удален, в противном случае - значение -1.

Функция переименования файла:

int rename(const char *filename, const char *newname);

Первый параметр filename - имя существующего файла (или директории), который необходимо переименовать. Второй параметр newname - новое имя файла (или директории), файл с данным именем не должен существовать. Функция возвращает нулевое значение, если файл был успешно переименован, в противном случае - значение -1.

Лекция 12. Сравнение С и С++. Потоковый ввод-вывод.

Новые синтаксические элементы языка C++ по сравнению с языком C.

С++ базируется на языке С. Полная совместимость гарантирует, что С++ полностью включает синтаксис С. Новыми синтаксическими элементами языка С++, отличающими его от языка С, являются:

  • декларации классов и объектов (и все, что с ними связано);

  • перегрузки операторов;

  • пространства имен;

  • декларации шаблонов функций и классов;

  • новые операторы для управления созданием и удалением объектов (new, delete);

  • ссылки на объекты и переменные;

  • механизм обработки исключений.

Языковые средства описания классов и объектов.

Важнейшим расширением для языка С является новый тип данных - class, который имеется в С++. Классы позволяют объединить в единый тип данных как данные-члены, так и функции члены. Классы могут предоставлять различные права доступа к различным членам и также ограничивают область видимости идентификаторов, которые в них определены.

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

Так, несмотря на то, что мы реально делаем в объектно-ориентированном программировании – это создание новых типов, фактически все объектно-ориентированные языки используют ключевое слово class. Когда вы видите слово “тип”, то думайте “класс” и наоборот.

Так как класс описывает набор объектов, которые имеют идентичные характеристики (элементы данных) и черты поведения (функциональность), класс реально является типом данных, потому что, например, число с плавающей точкой также имеет набор характеристик и черт поведения.

Как только класс создан, можно создать столько объектов этого класса, сколько захотите, а затем манипулировать этими объектами так, как если бы они являлись элементами, которые существуют в проблеме, которую требуется решить.

Но как заставить объект стать полезным для вас? Должен существовать способ сделать запрос к объекту, чтобы он что-то сделал, например, что-то нарисовал на экране. Каждый объект может удовлетворять только определенные запросы. Запросы, которые вы можете сделать к объекту, определяются его интерфейсом и типом, который определяет интерфейс.

В качестве типа класса могут быть использованы ключевые слова class, struct и union. Базовые классы - это список имен классов, разделяемых запятыми, элементы которых наследуются вновь определяемым классом. Доступ производного класса к наследуемым элементам родительского может указываться модификатором доступа - public, protected и private.

Перегрузка операторов. Программист может определять смысл операций при их применении к объектам определенного класса. Кроме арифметических, можно определять еще и логические операции, операции сравнения, вызова () и индексирования [], а также можно переопределять присваивание и инициализацию. Можно определить явное и неявное преобразование между определяемыми пользователем и основными типами.

Пространство имен. Пространство имён (англ. namespace) — некоторое множество, под которым подразумевается модель, абстрактное хранилище или окружение, созданное для логической группировки уникальных идентификаторов (то есть имён). Идентификатор, определенный в пространстве имён, ассоциируется с этим пространством. Один и тот же идентификатор может быть независимо определён в нескольких пространствах. Таким образом, значение, связанное с идентификатором, определённым в одном пространстве имён, может иметь (или не иметь) такое же (а скорее, другое) значение, как и такой же идентификатор, определённый в другом пространстве. Языки с поддержкой пространств имён определяют правила, указывающие, к какому пространству имён принадлежит идентификатор (то есть его определение).

Механизм обработки исключений. С++ обеспечивает встроенный механизм обработки ошибок, называемый обработкой исключительных ситуаций. Благодаря обработке исключительных ситуаций можно упростить управление и реакцию на ошибки времени исполнения. Обработка исключительных ситуаций в С++ строится с помощью трех ключевых слов: try, catch, throw.

Потоковый ввод-вывод. Иерархия потоковых классов.

В отличие от стандартной библиотеки (в которой находятся средства, например, для работы со строками, или математические функции), унаследованной компиляторами языка С++ от языка С, библиотека ввода-вывода С++ является не библиотекой функций, а библиотекой классов. Это первая "промышленная" библиотека классов, разработанная для распространения совместно с компиляторами. Именно эту библиотеку рекомендуют изучать, начиная знакомиться с принципами объектно-ориентированного программирования. Одним из базовых принципов ООП является предположение о том, что объекты "знают", что нужно делать при появлении обращения (сообщения) определенного типа, т.е. для каждого типа адресованного ему обращения объект имеет соответствующий механизм обработки. Если мы используем объект cout, представляющий выходной поток, то как уже неоднократно показано на примерах, для каждого из базовых типов (int, long, double, ...) этот объект cout выбирает соответствующую процедуру обработки и выводит значение в соответствующем виде. Объект cout не может перепутать и вывести, например, целое число в формате с плавающей точкой. От таких ошибок, которые были возможны в языках С или FORTRAN, когда программист сам определял форму внешнего представления, библиотека классов ввода-вывода хорошо защищена.

Библиотека потоковых классов построена на основе двух базовых классов: ios и streambuf. Класс streambuf обеспечивает буферизацию данных во всех производных классах, которыми явно или неявно пользуется программист. Обращаться к его методам и данным из прикладных программ обычно не нужно. Класс streambuf обеспечивает взаимодействие создаваемых потоков с физическими устройствами. Он обеспечивает производные классы достаточно общими методами для буферизации данных. Класс ios и производные классы содержат указатель на класс streambuf, но об этом можно до времени не вспоминать. Методы и данные класса streambuf программист явно обычно не использует. Этот класс нужен другим классам библиотеки ввода-вывода. Он доступен и программисту-пользователю для создания новых классов на основе уже существующего класса из iostream. Однако необходимость в построении таких производных классов возникает достаточно редко, и мы не будем рассматривать класс streambuf. Класс ios содержит компоненты (данные и методы), которые являются общими, как для ввода, так и для вывода.

При работе с потоковой библиотекой ввода-вывода программист обычно достаточно активно использует следующие классы:

  • ios - базовый потоковый класс;

  • istream - класс входных потоков;

  • ostream - класс выходных потоков;

  • iostream - класс двунаправленных потоков ввода-вывода;

  • istrstream - класс входных строковых потоков;

  • ostrstream - класс выходных строковых потоков;

  • strstream - класс двунаправленных строковых потоков (ввода-вывода);

  • ifstream - класс входных файловых потоков;

  • ofstream - класс выходных файловых потоков;

  • fstream - класс двунаправленных файловых потоков (ввода-вывода);

  • constream - класс консольных выходных потоков.

Диаграмма взаимозависимости перечисленных классов изображена на рисунке 1. Следует отметить, что эта диаграмма потоковых классов упрощена. В реальной схеме присутствуют промежуточные классы и реализовано более сложное множественное наследование. Кроме того, программист, как упоминалось, обычно не учитывает наличия второго базового класса streambuf, и он не показан на схеме.

Как наглядно видно из диаграммы классов, класс ios является базовым для классов ostream, istream, и опосредовано базовым для всех остальных потоковых классов. Все общие средства потоковых классов помещаются в класс ios. Например, при помощи методов и данных класса ios осуществляется управление процессом передачи символов из буфера и в буфер. При выполнении этих действий необходимы, например, сведения о нужном основании счисления (восьмеричное, десятичное, шестнадцатеричное), о точности представления вещественных чисел, и т.д. Класс ios содержит эти сведения, т.е. (методы) функции и данные, относящиеся к состояниям потоков и позволяющие менять их свойства.

Рис. 11.1. Упрощенная схема иерархии потоковых классов

       Потоковые классы, их данные и методы становятся видимыми и доступными в программе, если в нее включен нужный заголовочный файл:

  • iostream.h - для классов ios, istream, ostream, stream;

  • strstrea.h - для классов istrstream, ostrstream, strstream;

  • fstream.h - для классов ifstream, ofstream, fstream;

  • constrea.h - для класса constream.

    Так как класс ios является базовым для остальных потоковых классов, то включение в текст программы любого из заголовочных файлов strstrea.h, constrea.h или fstream.h автоматически подключает к программе файл iostream.h. Соответствующие проверки выполняются на этапе препроцессорной обработки.