Из программы данные можно отправить (записать) в поток вывода, а получить (прочитать) их в программе из потока ввода. Например, сразу после запуска программы, поток стандартного ввода "cin" подключается к клавиатуре, а поток стандартного вывода "cout" – к экрану.
Список функций для работы с файловыми потоками хранится в заголовочном файле "fstream.h". Поэтому во всех рассматриваемых ниже фрагментах программ предполагается, что в начале программы есть соответствующая директива "#include":
#include<fstream.h>. В программе перед первым обращением к потоку ввода или вывода необходимо "создать" поток. Операторы для создания потоков похожи на описания переменных, и они обычно размещаются в начале программы или функции рядом с описаниями переменных. Например, операторы ifstream in_stream;
ofstream out_stream;
создают поток с именем "in_stream", являющийся объектом класса (как типа данных) "ifstream" (input-file-stream, файловый поток ввода), и поток с именем "out_stream", являющийся объектом класса "ofstream" (output-file-stream, файловый поток вывода).
После создания потока его можно подключить к файлу (открыть файл) с помощью функции open(...). Функция "open(...)" у потоков ifstream и ofstream работает по-разному. Для подключения потока ifstream с именем "in_stream" к файлу с именем "Lecture.txt" надо применить следующий вызов:
in_stream.open("Lecture.txt");
Чтобы к файлу "Lecture.txt" подключить поток вывода ofstream с именем "out_stream", надо выполнить аналогичный оператор:
out_stream.open("Lecture.txt");
Этот оператор подключит поток "out_stream" к файлу "Lecture.txt", но при этом прежнее содержимое файла будет удалено. Файл будет подготовлен к приему новых данных. Для отключения потока "in_stream" от файла, к которому он подключен (для закрытия файла), надо вызвать функцию close():
in_stream.close();
35. Работа с файлами в С++: определение конца файла, проверка ошибок при выполнение файловых операций, закрытие файла.
Для отключения потока "in_stream" от файла, к которому он подключен (для закрытия файла), надо вызвать функцию close():
in_stream.close();
Функция отключения от файла у потока вывода:
out_stream.close();
выполняет аналогичные действия, но, дополнительно, в конец файла добавляется служебный символ "end-of-file (маркер конца файла)". Т.о., даже если в поток вывода не записывались никакие данных, то после отключения потока "out_stream" в файле "Lecture.txt" будет один служебный символ. В таком случае файл "Lecture.txt" останется на диске, но он будет пустым.
Файловые операции, например, открытие и закрытие файлов, известны как один из наиболее вероятных источников ошибок. В надежных коммерческих программах всегда выполняется проверка, успешно или нет завершилась файловая операция. В случае ошибки вызывается специальная функция-обработчик ошибки.
Простейший способ проверки ошибок файловых операций заключается в вызове функции fail(). Вызов
in_stream.fail();
возвращает истинное значение (True), если последняя операция потока "in_stream" привела к ошибке (может быть, была попытка открытия несуществующего файла). После ошибки поток "in_stream" может быть поврежден, поэтому лучше не продолжать работу с ним.
В приведенном ниже фрагменте программы в случае ошибки при открытии файла на экран выдается сообщение и программа завершает работу с помощью библиотечной функции "exit()" (она описана в файле "stdlib.h"):
#include <iostream.h>
#include <fstream.h>
#include <stdlib.h>
#include <conio.h>
int main() {
ifstream in_stream;
in_stream.open( "Lecture.txt" );
if ( in_stream.fail() ) {
cout << "Извините, открыть файл не удалось!\n";
getch();
exit(1);}
36. Работа с файлами в С++: управление открытием файла, символьный ввод/вывод, ввод/вывод строки.
В программе перед первым обращением к потоку ввода или вывода необходимо "создать" поток. Операторы для создания потоков похожи на описания переменных, и они обычно размещаются в начале программы или функции рядом с описаниями переменных. Например, операторы
ifstream in_stream;
ofstream out_stream;
создают поток с именем "in_stream", являющийся объектом класса (как типа данных) "ifstream" (input-file-stream, файловый поток ввода), и поток с именем "out_stream", являющийся объектом класса "ofstream" (output-file-stream, файловый поток вывода).
После создания потока его можно подключить к файлу (открыть файл) с помощью функции open(...). Функция "open(...)" у потоков ifstream и ofstream работает по-разному.
Для подключения потока ifstream с именем "in_stream" к файлу с именем "Lecture.txt" надо применить следующий вызов:
in_stream.open("Lecture.txt");
1.4.1 Функция ввода get()
После того, как файл для ввода данных открыт, из него можно считывать отдельные символы. Для этого служит функция get(). У нее есть параметр типа char&. Если программа находится в начале файла, то после вызова:
in_stream.get(ch);
переменной "ch" будет присвоено значение первой буквы файла и поток "in_stream" будет подготовлен для чтения следующего символа.
1.4.2 Функция вывода put()
С помощью потока вывода класса ofstream в открытый файл можно записывать отдельные символы. Для этого у класса ofstream есть функция put(). Записываемый символ передается ей как параметр типа "char".
37. Типы данных определяемые пользователем: перечисления. Переименование типов.
Перечисление-тип, группирующий набор именованных целочисленных констант.
При определения перечисления используется ключевое слово enum, за которым следует необязательное имя перечисления и заключенный в фигурные скобки список перечислителей, т.е. допустимых значений, разделенных запятыми. По умолчанию первый перечислитель получает нулевое значение, а каждый последующий- значение, которое на единицу больше. Одному или нескольким перечислителям можно присвоить конкретные значения. Используемое для инициализации перечислителя значение должно быть константным выражением. Значение перечислителя изменить нельзя.
38. Работа со структурами в С++: определение, описание и инициализация структур.
Структура – это составной тип данных, который получается путем объединения компонент, принадлежащих к другим типам данных (возможно, тоже составным). Структура предназначена для объединения нескольких переменных в один тип данных. Сначала тип структуры надо описать, а затем можно создавать переменные этого нового типа. Описание типа структуры "T" имеет следующий синтаксис:
struct T {
T1 var1;
T2 var2;
... T3 var3; };
где "T1", "T2", "T3" – имена встроенных типов данных ("int", "char" и др.) или других составных типов. "var1", "var2", "var3" – это имена внутренних переменных структуры. Элементы структуры называются полями структуры и могут иметь любой тип, кроме типа этой же структуры, но могут быть указателями на него. Если отсутствует имя типа, должен быть указан список описателей переменных, указателей или массивов. Обратите внимание на точку с запятой в конце описания типа структуры.
Важно отметить, что не все возможные комбинации значений компонент структуры могут иметь смысл применительно к конкретной задаче. Например, тип "Date", определенный выше, включает значения {50, 5, 1973} и {100, 22, 1815}, хотя дней с такими датами не существует. Т.о., определение этого типа не отражает реального положения вещей, но все же оно достаточно близко к практическим целям. Ответственность за то, чтобы при выполнении программы не возникали подобные бессмысленные значения, возлагается на программиста. Обращения к компонентам классов в объектно-ориентированном Си++ очень похожи на обращения к компонентам структур.
Переменные типа структуры можно присваивать, передавать, как параметры функции, и возвращать из функции в качестве результата. Например:
Person current;
Person set_current_person( Person& p )
{Person prev = current;
current = p;
return prev; }
Остальные операции, такие, как сравнение ("==" и "!="), для структур по умолчанию не определены, но программист может их определить при необходимости.
Имя типа структуры можно использовать еще до того, как этот тип будет определен, вплоть до момента, когда потребуется, чтобы стал известен размер структуры.
39. Работа со структурами в С++: доступ к компонентам структуры через указател, массивы и структуры.
1.3 Доступ к компонентам структуры через указатель
Аналогично обычным переменным, можно создавать указатели на переменные-структуры. Для доступа к компонентам структуры через указатель применяется операция "->". Например:
void print_person( Person* p ) {
cout << p->name << '\n';
cout << p->birthdate.day << '\n';
cout << p->birthdate.month << '\n';
cout << p->birthdate.year << '\n';
cout << p->salary << '\n\n'; }
Функцию "print_person()" можно переписать в эквивалентном виде с помощью операции разыменования указателя "*" и операции доступа к компонентам структуры ".". Обратите внимание на скобки в записи "(*p).", которые необходимы, поскольку приоритет операции "." выше, чем у "*":
void print_person( Person* p ) {
cout << (*p).name << '\n';
cout << (*p).birthdate.day << '\n';
cout << (*p).birthdate.month << '\n';
cout << (*p).birthdate.year << '\n';
cout << (*p).salary << '\n\n'; }
У массива и структуры есть общее свойство: оба этих типа данных являются типами с произвольным доступом. Но структура более универсальна, поскольку не требуется, чтобы типы всех ее компонент были одинаковы. С другой стороны, в некоторых случаях массив предоставляет бóльшие возможности, т.к. индексы его элементов могут вычисляться, а имена компонент структуры – это фиксированные идентификаторы, задаваемые в описании типа.