Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ЛАБ_ПРОГР.doc
Скачиваний:
33
Добавлен:
29.02.2016
Размер:
1.66 Mб
Скачать

Лабораторная работа № 17

Тема: «Работа с двоичными файлами»

  1. Цель работы

1.1 Получение навыков в программировании ввода и вывода с использованием файлов.

1.2 Получение навыков обработки числовых данных, содержащихся в файлах.

  1. Техническое обеспечение

    1. Персональная ЭВМ IBM PC/286 и более поздних моделей.

    2. Клавиатура.

    3. Дисплей.

    4. Печатающее устройство.

  1. Программное обеспечение

    1. Операционная система Windows

    2. Система программирования Visual C++ версия 6.0 или Borland C++ версия 3.1 и более поздние версии.

  1. Постановка задачи

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

  1. Содержание отчета

5.1. Тема и цель работы.

5.2. Схема алгоритма решения задачи.

5.3. Текст программы.

5.4. Результаты выполнения программы.

  1. Общие сведения

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

    1. Потоки

Хотя каждое устройство ввода-вывода ЭВМ отличается от другого, система ввода-вывода преобразует их в единое логическое устройство поток. Все потоки похожи своим поведением. Поскольку потоки не зависят от устройств, одни и те же функции могут записывать информацию в файл на диске и использоваться для записи на другое устройство, например, на консоль. Существуют два типа потоков –текстовыеидвоичные.

Текстовые потоки– это последовательность символов. В текстовых потоках некоторые символы могут преобразовываться согласно требованиям среды. Например, символ новой строки (‘\n’) может преобразовываться в пару «возврат каретки – перевод строки». Следовательно, может не быть однозначного соответствия между записываемыми и считываемыми символами и символами во внешнем устройстве.

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

    1. Ф а й л ы

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

Не все файлы имеют одинаковые возможности. Например, дисковый файл поддерживает произвольный доступ, а модем – нет. Это иллюстрирует важный момент системы ввода-вывода языка С – все потоки одинаковы, а файлы нет.

Если файлы поддерживают произвольныйдоступ, открытие файла инициализируетуказатель положения в файлена начало файла. По мере чтения или записи в файл указатель положения перемещается, обеспечивая тем самым продвижение по файлу.

Наименьшей частью доступа к диску является сектор. Информация записывается на диск или читается с диска посекторно. Следовательно, даже если программе необходим 1 байт данных, будет прочитан весь сектор. Эти данные помещаются вбуфер– область памяти, пока не смогут использоваться программой.

Связь потока с файлом уничтожается с помощью операции закрытия. Закрытие потока вызывает принудительный сброс всего содержимого буфера во внешнее устройство. Данный процесс называется очисткойбуфера и он гарантирует, что в буфере не останется информации. Все файлы закрываются автоматически, когда программа завершается нормальным образом, выходя из функцииmain()или с помощью вызова функцииexit(). Тем не менее, лучше самому закрыть файлы, используя функциюfclose() в тот момент, когда файл уже не нужен.

В начале работы программы открыты пять предопределенных текстовых потоков: stdin,stdout,stderr,stdaux,stdprn. Они соответствуют следующим стандартным устройствам ввода-вывода:

ПотокУстройство

stdin Клавиатура

stdout Экран

stderr Экран

stdaux Первый последовательный порт (COM1)

stdprn Принтер

    1. Указатель на файл

Указатель на файл– это указатель на информацию, определяющую различные параметры файла, включая его имя, состояние и текущую позицию. В принципе, указатель на файл идентифицирует конкретный дисковый файл и используется потоком для выполнения операций ввода-вывода. Указатель на файл – это переменная-указатель типаFILE(тип, определенный в файлеstdio.h). Чтобы прочитать или записать файлы, программе надо использовать указатели на файлы. Для создания файловой переменной-указателя используется оператор типа:

FILE *fp;

    1. Открытие файла

Функция fopen()открывает для использования поток, связывает файл с данным потоком и затем возвращает указатель типаFILEна данный поток. Функцияfopen()имеет прототип:

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

где filenameуказывает на строку, содержащую имя файла, аmodeуказывает на строку, содержащую желаемый режим открытия файла (таблица 1).

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

Режим

З н а ч е н и е

r

Открывает файл для чтения (по умолчанию текстовый )

w

Открывает файл для записи (по умолчанию текстовый )

a

Присоединяет к файлу (по умолчанию текстовый )

rb

Открывает двоичный файл для чтения

wb

Открывает двоичный файл для записи

ab

Присоединяет (открывает для добавления)

r+”

Открывает файл для чтения/записи (по умолчанию. текстовый)

w+”

Создает файл для чтения/записи (по умолчанию текстовый)

a+”

Присоединяет или создает файл для чтения/записи (по умолчанию текстовый)

r+b

Открывает двоичный файл для чтения/записи

w+b

Создает двоичный файл для чтения/записи

a+b

Присоединяет или создает двоичный файл для чтения/записи

rt

Открывает текстовый файл для чтения

wt

Создает текстовый файл для записи

at

Присоединяет к текстовому файлу

r+t

Открывает текстовый файл для чтения

w+t

Создает текстовый файл для чтения/записи

a+t

Открывает или создает текстовый файл для чтения/записи

Примечание. При использованииrилиaпоток должен существовать.

Функция fopen()возвращает указатель базового типаFILE. Данный указатель идентифицирует файл и используется большинством функций файловой системы. Его никогда не следует изменять самостоятельно. Функцияfopen()возвращает нулевой указатель (NULL), если файл не может быть открыт.

Если необходимо открыть файл с именем “a:\file1.dat” на запись, следует записать:

FILE *fp;

. . .

fp = fopen(“a:\file1.dat”, “w”);

или лучше

FILE*fp;

. . .

if ((fp = fopen(“a:\file1.dat”, “w”) = = NULL) {

puts(“Не могу открыть файл”)ж

exit(1); }

elseputs(“Файл открыт для записи”);

Данный способ позволяет обнаружить ошибки при открытии файла, например, наличие защиты от записи на дискете или отсутствие свободного места.

Если fopen() используется для открытия файла на запись, то любой ранее существующий файл с указанным именем будет удален. Если его не существует, то он будет создан.

Для дозаписи информации в файл следует использовать режим “a”. Если файл не существует, то он будет создан.

Открытие файла на чтение требует наличия файла. Если файл не существует, то будет возвращена ошибка.

Если файл открыт для операции чтения/записи, то он не удаляется при наличии и, если его нет, то создается.

    1. Перенаправление потока

Указатели стандартных потоков stdin,stdout,stderr,stdaux,stdprnявляются константами, поэтому им нельзя присваивать новые значения указателя потока. Однако любой из этих потоков можно с помощью функцииfreopen() переадресовать на новое устройство или в другой поток, связанный с файлом на диске, например:

freopen(“file1.dat”,”w”,stdout);

Прототип функции freopen()

FILE *freopen (const char *имя_файла, const char *режим, FILE *поток);

Этот оператор закрывает поток для стандартного вывода и переназначает stdoutфайлуfile1.dat.

Пример. Данная программа использует freopen()для перенаправления потокаstdoutв текстовый файлfile1.msg. Здесь первыйprintf()выводит сообщение на экран, а второйprintf()записывает сообщение в файлfile1.msg, расположенный на диске.

#include <stdio.h>

#include <stdlib.h>

int main (void)

{

FILE*fp;

printf(“Это сообщение выводится на дисплей”);

if ((fp = freopen(“file1.msg”, “w”, stdout)) = = NULL) {

printf(“Не могу открыть файл”);

exit(1);

}

printf(“Это сообщение выводится в файлfile1.msg”);

fclose(fp);

return 0;

}

    1. Чтение из потока и запись в поток

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

Классификация функций чтения и записи по типу объекта, над которым выполняется операция, и разновидности потока приведены в таблице 2.

Операция чтения и записи для потока начинается всегда с текущей позиции потока, определяемой указателем положения в файле. Начальное положение указателя положения в файле устанавливается при открытии потока и может указывать на начало или конец потока в зависимости от того, с каким режимом доступа открыт поток (режимы “r” и “w” устанавливают поток на начало, а режим “a” на конец). После выполнения операции чтения или записи указатель положения в файле изменяется для того, чтобы отразить новую текущую позицию потока. Например, если из потока был прочитан один символ, указатель положения в файле увеличивается на единицу, чтобы следующая операция чтения начиналась с первого байта после прочитанного.

Таблица 2 – Классификация функций чтения и записи потока

Объект операции

Операция ввода-вывода

Чтение

Запись

из потока stdin

из любого потока

из строки Си

в поток stdout

в любой поток

в строку Си

Последователь-ность байтов

fread()

fwrite()

Отдельный символ

getchar()

fgetchar()

putchar()

fputchar()

Данное типа int

getw()

putw()

Строка

gets()

fgets()

puts()

fputs()

Форматирован-ные данные

scanf()

fscanf()

sscanf()

printf()

fprintf()

sprintf()

Указатель положения в файле можно позиционировать на любое место в потоке. Следующая операция ввода-вывода будет выполняться с указанной позиции, т. е. Позиционирование позволяет осуществлять произвольный ввод-вывод для потока. Библиотека функций языка С предоставляет пять функций для позиционирования указателя положения в файле:

ftell(),fgetpos() – получают текущую позицию указателя положения в файле;

fseek(),fsetpos() – устанавливают текущую позицию указателя;

rewind() – позиционирует указатель на начало потока.

Функция

long ftell (FILE *fp);

возвращает текущее значение указателя позиции в файле для указанного потока. Это значение представляет собой количество байт, на которое указатель отстоит от начала файла. В случае ошибки ftell() возвращает значение1L.

Пример.

long i;

if ((I = ftell(fp) = = 1L) printf(“Ошибка”);

Функция

int fseek (FILE *fp, long число_байт, int начало);

устанавливает указатель положения в файле, связанном с fp, в соответствии со значениямичисло_байтиначало. Здесь:число_байт– это длинное целое, содержащее число байт от параметраначалодо позиции указателя;начало– это одно из следующих макроопределений, установленных вstdio.h:

Имя Значение Позиция

SEEK_SET0 Начало файла

SEEK_CUR1 Текущая позиция

SEEK_END 2 Конец файла

В случае успеха функция fseek() возвращает значение 0, в случае неудачи возвращает ненулевое значение.

П р и м е р ыиспользования функцииfseek():

FILE *fp;

long n;

. . .

fseek(fp, 0L,SEEK_SET); /* позиционирование на начало потока */

fseek(fp, 0L,SEEK_END); /* позиционирование на конец потока */

fseek(fp,nL,SEEK_SET); /* позиционирование наnбайт от начала потока */

fseek(fp,nL,SEEK_CUR); /* позиционирование наnбайт от текущей позиции */

fseek(fp,nL, 1); /* позиционирование наnбайт до текущей позиции */

fseek(fp, ,nL, 2); /* позиционирует наnбайт до конца потока */

fseek(fp, ,nL, 2); /* указатель может быть позиционирован за конец потока */

fseek(fp, -nL, 0); /* Ошибка. Попытка установить указатель перед началом */

Функция rewind() устанавливает указатель положения в файле на начало файла. Эквивалентна функции

fseek(fp, 0L,SEEK_SET);

Имеет прототип

void rewind (FILE *fp);

Функцииfgetpos() иfsetpos() обычно используются в паре. Функцияfgetpos() хранит текущее значение указателя положения в файле, связанном с указателемfp, в некоторой переменной, на которую указываетномер_позиции. Прототип этой функции:

int*fgetpos(FILE*fp,fpos_tномер_позиции);

Функция fsetpos() устанавливает указатель положения в файле, связанном с потокомfp, в позицию, указанную параметромномер_позиции. Прототип этой функции:

int fsetpos (FILE *fp, const fops_t *номер_позиции);

Тип fops_tопределен вstdio.hи соответствует типуlong int.

В случае случае успешного завершения функции fgetpos() иfsetpos() возвращают значение 0 и при неудаче возвращается ненулевое значение.

Приведенная ниже функция читает группу байтов с использованием fgetpos() иfsetpos(). Позиция указателя положения в файле не меняется.

# include<stdio.h> /* требуется для объявления функции */

long read_bytes (FILE *fp, long n, char *save)

{

longnum, /* число прочитанных байт */

pos; /* текущая позиция указателя положения */

num=1L;

if(fgetpos(fp, &pos) = = 0) /* сохранить текущую позицию указателя */

num = fread(save, 1, num, fp); /* прочитать n байт */

fsetpos(fp, &pos); /* восстановить положение указателя положения */

return num;

}

Если поток открывается для добавления (режим доступа “a”, “a+” и т. д.) операции записи относятся к концу потока, хотя указатель положения может быть переустановлен. Это предохраняет данные в файле от затирания.

Нельзя использовать функции определения и установки указателя положения в файле для потока, представляющего устройство. Использование функций ftell(),fseek(),rewind(),fgetpos() иfsetpos() с любым из стандартных потоков приводит к неопределенным результатам.

    1. Закрытие потока

Для закрытия потока, ранее открытого функцией fopen(), используются функцииfclose() иfcloseall(). Функцияfclose() закрывает отдельный указанный поток; функцияfcloseall() закрывает все открытые потоки за исключением стандартных. Все буфера, связанные с потоком перед закрытием выгружаются. Если поток явно не закрывается в программе, он автоматически закрывается при завершении программы. Однако лучше закрыть поток сразу, если с ним закончена работа, так как количество одновременно открытых файлов в операционной системе ограничено. Система программирования языка С разрешает иметь одновременно до 20 открытых файлов.

Прототип функции fclose()

int fclose (FILE *fp);

где fp– указатель на файл, возвращенныйfopen().

Если функция fclose() возвращает 0, то операция закрытия прошла успешно а еслиEOF, то была ошибка (например, отсутствие носителя).

Прототип функции fcloseall()

int fcloseall (void);

Прототипы функцийfclose() иfcloseallопределены в файлеio.h.

    1. Использование функций feof() и ferror()

Когда файл открыт для двоичного ввода, может быть прочитано целочисленное значение равное EOF(значениеEOFобозначает признак конца файла), а не признак конца конца файла. В результате функцияfgetc() (чтение символа из файла) будет отображать признак конца файла, хотя физический конец еще не достигнут. Для разрешения этой проблемы в языке С имеется функцияfeof(), используемая для определения конца двоичного файла. Она имеет прототип:

int feof ((FILE *fp);

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

while (! Feof (fp)) ch = getc(fp);

Данный метод может применяться и к текстовым файлам.

Функция ferror() используется для определения, привела ли выполняемая операция к ошибке. Она имеет прототип:

int ferror (FILE *fp);

Она возвращает истину, если в результате выполнения операции над файлом произошла ошибка, в противном случае – ложь. Поскольку каждая файловая операция изменяет состояние ошибки, ferror() должна вызываться после каждой файловой операции.

  1. Варианты заданий

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

- упорядочить файл по возрастанию значений компонентов;

- найти сумму четных чисел среди компонентов;

- подсчитать количество полных квадратов среди компонентов.

Результаты записать в текстовый файл.

2) Создать файл целых чисел (не менее 25). Создать на базе этого файла три файла – F1,F2 иF3. В файлF1 поместить в порядке возрастания положительные компоненты, в файлF2 поместить в порядке убывания отрицательные компоненты, в файлF3 поместить все нулевые компоненты.

Число компонент в каждом файле вывести в текстовый файл.

3) Создать три файла F1,F2 иF3, состоящие каждый не менее чем из 8 компонент целого типа. Упорядочить каждый файл по убыванию значений компонентов. Объединить файлыF1,F2 иF3 в один файл в последовательности: компонент файлаF2, компонент файлаF1, компонент файлаF3, далее вновь очередной компонент файлаF2 и т. д.

Недостающие компоненты файлов F1,F2 иF3 заменяются соответственно значениями: 1, -1 и 0 соответственно.

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

  • найти сумму компонент файла;

  • найти произведение компонент файла;

  • найти сумму квадратов компонент файла;

  • найти среднее арифметическое и среднее геометрическое компонент файла;

  • найти третью от конца компоненту файла.

Все результаты записать в текстовый файл.

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

  • найти наибольшее из значений компонент с четными номерами;

  • найти наименьшее из значений компонент с нечетными номерами;

  • сумму положительных компонент;

  • разность наибольшей по модулю компоненты и наименьшей по модулю;

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

Все результаты записать в текстовый файл.

6) Интегральный синус

вычисляется разложением в ряд

.

Составить таблицу значений интегрального синуса для 0.1 х2 (х изменяется с шагом 0.1). Результаты записать построчно в текстовый файл в виде

“x = 0.1 Si(0.1) = a0 + a1+ . . . an = аi”,

где a0,a1, …,an– численные значения членов ряда;аi - численное значение суммы членов ряда.

Точность вычисления оценивается по последнему члену ряда и равна =0.00001.

7) Даны файлы F1,F2,F3,F4,F5, компоненты которых являются вещественными числами. Организовать обмен компонентами между файлами в соответствии со следующей схемой:F1F3,F2F4,F3F5,F4F2,F5F1, т. е. компоненты файлаF1 переписываются в файлF3, компоненты файлаF2 – вF4 и т. д. Разрешается использовать только один вспомогательный файлG.

8) Дан файл F, компоненты которого являются целыми числами. Никакой из компонентов файла F не равна нулю. Числа в файле идут в следующем порядке: десять положительных, десять отрицательных, десять положительных, десять отрицательных и т. д. Число компонент файла должно быть кратно 40. Переписать компоненты файла F в файл G, чтобы в файле G числа шли в следующем порядке: пять положительных, пять отрицательных, пять положительных, пять отрицательных и т. д.

9) Дан файл F, компоненты которого являются целыми числами. Никакая из компонент файлаFне равна нулю. Числа в файле идут в следующем порядке: десять положительных, десять отрицательных, десять положительных, десять отрицательных и т. д. Число компонент файла должно быть кратно 40. Переписать компоненты файлаFв файлG, чтобы в файлеGчисла шли в следующем порядке: восемь положительных, восемь отрицательных, восемь положительных, восемь отрицательных и т. д.

10) Дан файл F, компоненты которого являются целыми числами. Никакая из компонент не равна нулю. ФайлFсодержит равное количество положительных и отрицательных чисел. Используя вспомогательный файлH, переписать компоненты файлаFв файлGтак, чтобы в файлеGне было двух соседних чисел с одинаковым знаком.

11) Дан файл F, компоненты которого являются целыми числами. Никакая из компонент не равна нулю. ФайлFсодержит равное количество положительных и отрицательных чисел. Используя вспомогательный файлH, переписать компоненты файлаFв файлGтак, чтобы в файлеGсначала шли положительные, а затем отрицательные числа.

12) Дан файл F, компоненты которого являются целыми числами. Никакая из компонент не равна нулю. ФайлFсодержит равное количество положительных и отрицательных чисел. Используя вспомогательный файлH, переписать компоненты файлаFв файлGтак, чтобы в файлеGчисла следовали в следующем порядке: два положительных, два отрицательных, два положительных, два отрицательных и т. д. (предполагается, что число компонент в файлеFкратно 4).

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]