Подход с использованием возможностей языка Си

Файлы

. . .

. . .

Использование функций для работы с производными типами данных

В соответствии с модульным подходом к программированию целесообразно реализовывать вопросы поиска в отдельных функциях, например:

void ZaprosFamily(student *k, char *f);

int main()

{ . . .

char *family;

cout<<”\nВведите фамилию для поиска: ”;

cin >>family;

ZaprosFamily(kurs, family);

return 0;

}

void ZaprosFamily(student *k, char *f)

{

k=0;

for (j=0; j<N; j++)

if ( strcmp(k[j].fam, f)==0 )

{

cout<<”\nФамилия: ”<<k[j].fam;

cout<<”\tИмя: ”<<k[j].name;

cout<<”\tВозраст: ”<<k[j].age;

k++;

}

if (k==0) cout<<”\nТаких студентов нет!”;

return;

}

В этом примере в функцию передаются два указателя: один типа student, другой типа char. Что же это за указатели, почему они передаются и что значат?

Первый указатель есть указатель на тип данных student, то есть на структуру. Почему передается указатель? Потому, что экземпляры структуры описаны в виде массива экземпляров, а, известно, что имя массива есть указатель на нулевой элемент массива. Таким образом, мы передаем в функцию через указатель весь массив экземпляров структуры.

Второй указатель есть указатель на тип данных char, то есть на символьный тип данных. Через этот указатель в функцию передается строка для поиска интересующей нас фамилии студента.

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

 


Лабораторная работа № 14
Применение структур для решения прикладных задач

ЦЕЛЬ РАБОТЫ: получение начальных навыков в объявлении, инициализации и использовании производных типов данных, совершенствование навыков описания и использования функций в программах.

Выполнение работы:в соответствии с вариантом составить и реализовать программы.

Задание I

 


Файлом называют способ хранения информации на физическом устройстве. Файл - это понятие, которое применимо ко всему - от файла на диске до терминала.

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

В отличие от Си, в С++ файловые потоки обслуживаются с использованием потоковых классов на основе библиотек <ifsream>и<ofstream>. Рассмотрим каждый из этих способов подробнее.

В Си существует два типа потоков: текстовые (text) и двоичные (binary).

Текстовый поток - это последовательность символов. При передаче символов из потока на экран, часть из них не выводится (например, символ возврата каретки, перевода строки).

Двоичный поток - это последовательность байтов, которые однозначно соответствуют тому, что находится на внешнем устройстве.

Прежде чем читать или записывать информацию в файл, он должен быть открыт и тем самым связан с потоком. Это можно сделать с помощью библиотечной функции fopen(). Она берет внешнее представление файла (например, c:\my_prog.txt) и связывает его с внутренним логические именем, которое используется далее в программе. Логическое имя - это указатель на требуемый файл. Его необходимо определить; делается это, например, так:

FILE *fp;

Здесь FILE - имя типа, описанное в стандартном заголовочном файле stdio.h, fp - указатель на файл. Обращение к функции fopen() в программе осуществляется выражением:

fp = fopen("спецификация_файла",

"способ_использования_файла");

Спецификация файла (т.е. имя файла и путь к нему) может, например, иметь вид: "c:\\my_prog.txt" - для файла my_prog.txt на диске с:.

Способ использования файла задается следующими символами:

· r – (от read) открыть существующий файл для чтения;

· w – (от write) создать новый файл для записи (если файл с указанным именем существует, то он будет переписан);

· а – (от add) дополнить файл (открыть существующий файл для записи информации, начиная с конца файла, или создать файл, если он не существует);

Тип файлового потока задается ключом t (текстовый) или b (бинарный). Если режим t или b не задан (например, r, w или а), то он определяется значением глобальной переменной _fmode. Если fmode=0_BINARY, то файлы открываются в двоичном режиме, а если _fmode=0_TEXT - в текстовом режиме. Константы 0_BINARY и 0_ТЕXТ определены в файле fcntl.h.

Если в результате обращения к функции fopen( ) возникает ошибка, то она возвращает указатель на константу NULL.

Рекомендуется использовать следующий способ открытия файла:

if ((fp = fopen("c:\\my_prog.txt", "rt")) == NULL)

{

puts("Открыть файл не удалось\n")

return 0;

}

После окончания работы с файлом он должен быть закрыт. Это делается с помощью библиотечной функции fclose(). Она имеет следующий прототип:

int fclose(FILE *fp);

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

Рассмотрим стандартные функции для работы с файлами последовательного доступа:

1. Функция putc() записывает символ в файл и имеет следующий прототип:

int putc(int с, FILE *fp);

Здесь fp - указатель на файл, возвращенный функцией fopen(), с - символ для записи (переменная с имеет тип int, но используется только младший байт). При успешном завершении putc() возвращает записанный символ, в противном случае возвращается константа EOF. Она определена в файле stdio.h и имеет значение (-1).

2. Функция getc() читает символ из файла и имеет следующий прототип:

int getc(FILE *fp);

Здесь fp - указатель на файл, возвращенный функцией fopen(). Эта функция возвращает прочитанный символ. Соответствующее значение имеет int, но старший байт равен нулю. Если достигнут конец файла, то getc возвращает значение ЕОF.

3. Функция feof() определяет конец файла при чтении двоичных данных и имеет следующий прототип:

int feof(FILE *fp);

Здесь fp - указатель на файл, возвращенный функцией fopen(). При достижении конца файла возвращается ненулевое значение, в противном случае возвращается 0.

4. Функция fputs() записывает строку символов в файл. Она отличается от функции puts() только тем, что в качестве второго параметра должен быть записан указатель на переменную файлового типа.

Например:

fputs("Ехаmple", fp);

При возникновении ошибки возвращается значение EOF.

5. Функция fgets() читает строку символов из файла. Она отличается от функции gets( ) тем, что в качестве второго параметра должно быть записано максимальное число вводимых символов плюс единица, а в качестве третьего - указатель на переменную файлового типа. Строка считывается целиком, если ее длина не превышает указанного числа символов, в противном случае функция возвращает только заданное число символов. Рассмотрим пример:

fgets(string, n, fp);

Функция возвращает указатель на строку string при успешном завершении и константу NULL в случае ошибки либо достижения конца файла.

Рассмотрим другие библиотечные функции, используемые для работы с файлами прямого доступа (все они описаны в файле stdio.h):

1. Функция fseek() позволяет выполнять чтение и запись с произвольным доступом и имеет следующий прототип:

int fseek(FILE *fp, long count, int access);

Здесь fp - указатель на файл, возвращенный функцией fopen(), count - номер байта относительно заданной начальной позиции, начиная с которого будет выполняться операция, access - способ задания начальной позиции.

Переменная accessможет принимать следующие значения:

0 - начальная позиция задана в начале файла;
1 - начальная позиция считается текущей;
2 - начальная позиция задана в конце файла.

При успешном завершении возвращается нуль, при ошибке - ненулевое значение.

2. Функция ferror() позволяет проверить правильность выполнения последней операции при работе с файлами. Имеет следующий прототип:

int ferror(FILE *fp);

В случае ошибки возвращается ненулевое значение, в противном случае возвращается нуль.

3. Функция remove( ) удаляет файл и имеет следующий прототип:

int remove(char *file_name);

Здесь file_name - указатель на строку со спецификацией файла. При успешном завершении возвращается нуль, в противном случае возвращается ненулевое значение.

4. Функция rewind() устанавливает указатель текущей позиции в начало файла и имеет следующий прототип:

void rewind(FILE *fp);

5. Функция fread() предназначена для чтения блоков данных из потока. Имеет прототип:

unsigned fread(void *ptr, unsigned size,

unsigned n, FILE *fp);

Она читает n элементов данных, длиной size байт каждый, из заданного входного потока fp в блок, на который указывает указатель ptr. Общее число прочитанных байтов равно произведению n*size. При успешном завершении функция fread() возвращает число прочитанных элементов данных, при ошибке - 0.

6. Функция fwrite() предназначена для записи в файл блоков данных. Имеет прототип:

unsigned fwrite(void *ptr, unsigned size,

unsigned n, FILE *fp);

Она добавляет n элементов данных, длиной size байт каждый, в заданный выходной файл fp. Данные записываются с позиции, на которую указывает указатель ptr. При успешном завершении операции функция fwrite() возвращает число записанных элементов данных, при ошибке - неверное число элементов данных.

15.2. Подход с использованием возможностей языка С++

Поток – это абстрактное понятие, относящееся к любому переносу данных от источника к приемнику. Потоки С++ в отличие от функций ввода/вывода в стиле Си основаны на понятии класса, которые мы пока не изучаем. Поэтому рассмотрим только порядок использования файловых потоков С++ без объяснения теории.

Файловый поток для чтения открывается следующим образом:

ifstream идентификатор_потока(имя_файла,ключи_потока);

Например:

ifstream input("input.txt",ios::in);

- открывается файловый поток с идентификатором input для чтения из файла. Такой файловый поток должен быть по аналогии с Си и обязательно закрыт: input.close();

Файловый поток для записи открывается так:

ofstream идентификатор_потока(имя_файла,ключи_потока);

Например:

ofstream output("output.txt",ios::out);

Этот поток также должен быть закрыт:

output.close();

Как же работать с такими потоками? Очень просто:

int main()

{

int n,k;

float f;

char str[100];

ifstream input("input.txt",ios::in);

ofstream output("output.txt",ios::out);

input>>n;

input>>k;

n=n+k;

output<<n<<endl;

input>>f;

f=f+2;

output<<f<<endl;

input>>str; output<<str<<”, “;

input>>str; output<<str<<”!!”;

input.close();

output.close();

return 0;

}

Если исходным содержимым файла input.txt было следующее:

-5.56783345

Hello world!

то в файл output.txt запишется следующее:

-3.56783

Hello, world!!!


Лабораторная работа № 15
Исследование методов доступа к файлам данных

ЦЕЛЬ РАБОТЫ: совершенствование навыков в программировании с использованием указателей, исследование функций последовательного и прямого доступа к файлам.

Выполнение работы:в соответствии с вариантом составить и реализовать программы.

Задание I