1. ЦЕЛЬ РАБОТЫ:изучение файловых типов данных, приобретение практических навыков создания и обработки текстовых файлов.
ОСНОВНЫЕ ПОНЯТИЯ
Файл - это упорядоченная последовательность однотипных компонентов, расположенных на внешнем носителе. Файлы предназначены только для хранения информации, а обработка этой информации осуществляется программами. Использование файлов целесообразно в случае:
n долговременного хранения данных;
n доступа различных программ к одним и тем же данным;
n обработки больших массивов данных, которые невозможно целиком разместить в оперативной памяти компьютера.
Файл, не содержащий ни одного элемента, называется пустым. Создается файл путем добавления новых записей в конец первоначально пустого файла. Длина файла, т.е. количество элементов, не задается при определении файла. При вводе и выводе данные рассматриваются как поток байтов. Физически поток – это файл или устройство (клавиатура или дисплей). В Си поток можно открыть для чтения и/или записи в текстовом или бинарном (двоичном) режиме. В текстовых файлах не употребляются первые 31 символ кодовой таблицы ASCII (управляющие), а символы конца строки 0x13 (возврат каретки, CR) и 0x10 (перевод строки LF) преобразуются при вводе в одиночный символ перевода строки \n (при выводе выполняется обратное преобразование). Эти символы добавляются в конце каждой строки, записываемой в текстовый файл. При обнаружении в текстовом файле символа с кодом 26 (0x26), т.е. признака конца файла, чтение файла в текстовом режиме заканчивается, хотя файл может иметь продолжение.
В двоичном режиме эти преобразования не выполняются, чтение продолжается, пока не встретится физический конец файла. При чтении символы не преобразуются и не анализируются.
Функция открытия потока fopen возвращает указатель на предопределенную структуру типа FILE (содержащую всю необходимую для работы с потоком информацию) при успешном открытии потока, или NULL в противном случае.
В заголовочном файле stdio.h содержится описание файлового типа FILE, с которым связывается файловая переменная (указатель на файл). При открытии файла указатель на файл связывается с конкретным файлом на диске и определяется режим открытия файла:
r (r+) - файл открывается для чтения (чтения и записи);
w (w+) - открывается пустой файл для записи (чтения и записи). Если файл с таким именем существует, он стирается;
a (а+)- файл открывается для дополнения в конец (чтения и дополнения).
Режим открытия может также содержать символы t (текстовый файл) и b (двоичный файл), указывающий на вид открываемого файла: rb, wb, ab, rt, at, rb+, wb+, ab+ и т.д.
Закрытие файла (текстового или бинарного) выполняется функцией fclose(), установка указателя на начало файла - функцией rewind(). Если при попытке чтения данных из файла встречается символ конца файла, то возвращается специальное значение EOF. Функции feof(),ferror() сообщают о причинах, по которым операция ввода/вывода не выполнилась. Запись данных в файл и чтение данных из файла можно выполнять разными способами:
1) функциями форматного ввода-вывода fscanf(), fprintf();
2) функциями неформатного ввода-вывода fread(), fwrite().
Если требуется сохранять и восстанавливать числовые данные без потери точности, то лучше использовать fread(), fwrite(). Если обрабатывается текстовая информация, которая будет просматриваться обычными текстовыми редакторами, то используется fgetс()- посимвольное чтение файла, посимвольная запись в файл - fputc() или функции fscanf(), fprintf(). Для чтения из файла и записи в файл строки используются функции fgets() и fputs().
Пример программы с использованием текстовых файлов
Создать текстовый файл "new1.txt" записав в него строку из 50 символов. Все символы, отличные от пробела, переписать в новый файл "new2.txt":
//lab9_1
#include <stdio.h>
#include <conio.h>
#include <string.h>
main()
{ char ch, sl[50];
char text[]="one to tree four";
FILE *pf, *pr; // Указатели на файлы
pf=fopen("new1.txt","w"); // Создание нового файла new1.txt
clrscr();
fprintf(pf,"%s\n",text); // Запись в файл строки text
fclose(pf); // Закрытие файла pf
pf=fopen("new1.txt","r"); // Открытие файла pf для чтения
pr=fopen("new2.txt","w"); // Создание нового файла new2.txt
while (!feof(pf)) // Пока не конец файла
{ ch=getc(pf); // Чтение символа ch из файла pf
if (ch != ' ')
putc(ch,pr); // Запись в файл pr символа ch
}
fclose(pr); // Закрытие файла pr
rewind(pf); // Возврат указателя на начало файла pf
fgets(sl,50,pf); // Чтение из файла pf строки в переменную sl
printf("%s\n",sl); // Вывод строки sl на дисплей
pr=fopen("new2.txt","r"); // Открытие файла pr для чтения
while (!feof(pr)) // Пока не конец файла pr
{ ch=getc(pr); // Чтение символа из файла pr
putchar(ch); // Вывод символа ch на дисплей
}
fclose(pf); // Закрытие файлов
fclose(pr);
getch();
}
При чтении текстовых файлов лучше использовать функции getc или fgetc, так как при использовании fscanf (pr, “%s”, &sl) читается только очередное слово до пробела или символа табуляции и требуется повторение этой функции многократно для других слов.
ВЫПОЛНЕНИЕ РАБОТЫ
3.1. Разобрать и проанализировать приведенную программу.
3.2 . Создать текстовый файл из 5 строк, прочитать созданный файл и получить новый файл согласно своему варианту.
Варианты заданий
1. Из строк все цифры переписать в новый файл.
2. Переписать все строки в новый файл, заменив пробелы на запятые.
3. Переписать в новый файл все символы из строк, отличные от цифр.
4. Подсчитать количество слов в каждой строке и записать их в новый файл.
5. Переписать все строки в новый файл, заменив все буквы 'м' на 'М'.
6. В новом файле заменить все цифры на восклицательные знаки.
7. Переписать все латинские буквы верхнего регистра из каждой строки в новый файл.
8. Переписать в новый файл строки, удалив из них все русские буквы нижнего регистра.
9. В новом файле заменить все латинские буквы верхнего регистра на буквы нижнего регистра.
10. Переписать в новый файл все строки, заменив все русские буквы нижнего регистра на буквы верхнего регистра.
11. Все слова, начинающиеся с гласных переписать в один файл, а с согласных – в другой новый файл.
12. В новый файл переписать каждую строку наоборот.
КОНТРОЛЬНЫЕ ВОПРОСЫ
4.1. Чем отличается файл от массива?
4.2. Особенности организации текстовых файлов.
4.3. Что понимается под чтением, и что под записью в файл?
Лабораторная работа № 10
Обработка бинарных файлов
1. ЦЕЛЬ РАБОТЫ:приобретение практических навыков создания и обработки бинарных файлов.
ОСНОВНЫЕ ПОНЯТИЯ
Если файл открыт в бинарном режиме, его можно записывать или считывать побайтно. Функция fseek() позволяет обращаться с файлом как с массивом и переходить к любой позиции в файле, обеспечивая возможность произвольного доступа. Если текстовые файлы являются файлами с последовательным доступом, то произвольный доступ чаще всего применяется к бинарным файлам.
Бинарные файлы могут содержать любую информацию. Чаще всего используются файлы, содержащие структуры. Для чтения и записи в бинарные файлы можно использовать функции fread(), fwrite() или fscanf(),fprintf().
fread –функция для чтения из файла:
int fread(void *ptr, unsigned size, unsigned count, FILE *f);
Из файла f считываются и по адресу ptr записываются count элементов размером size каждый. Функция возвращает число фактически считанных элементов.
fwrite – функция для записи в файл:
int fwrite(void *ptr, unsigned size, unsigned count, FILE *f);
В файл записываются, начиная с адреса ptr, count элементов размером size каждый. Функция возвращает число фактически записанных элементов.
fseek – функция для произвольного доступа к байтам бинарных файлов:
int fseek(FILE *f, long offset, int w);
offset показывает, на сколько байт нужно сместиться относительно точки отсчёта – w.
w должно быть равно одной из трех констант:
SEEK_SET или 0 - начало файла;
SEEK_CUR или 1 – текущая позиция в файле;
SEEK_END или 2 – конец файла.
ftell - возвращает текущую позицию в файле как длинное целое:
long int ftell (FILE *f);
Пример обработки бинарного файла
Составить программу, выполняющую следующие функции:
1. Создание нового файла;
2. Просмотр файла;
3. Добавление информации в конец файла;
4. Поиск по названию товара и изменение цены и количества;
Файл создать из структур вида: название товара, его цена и количество.
Задание выполнить в отдельных функциях. Использовать меню для выбора функций.
//lab10_1
#include <stdio.h>
#include <conio.h>
#include <string.h>
struct tov {char name[10]; float c; int kol;} t1;
void input(FILE *); // создание нового файла
void print(FILE *); // просмотр файла
void app(FILE *); // добавление в файл
void find(FILE *); // поиск и изменение
main()
{ char c;
FILE *tf;
while (1)
{ clrscr();
puts(" 1 – новый файл");
puts(" 2 – просмотр файла");
puts(" 3 – добавление в файл");
puts(" 4 – поиск и изменение");
puts(" 0 - выход");
c=getch();
switch(c)
{ case '1':input(tf);break;
case '2':print(tf);break;
case '3':app(tf);break;
case '4':find(tf);break;
case '0':return 0;
default : puts(" неверный режим");
}
}
}
void input(FILE *tf)
{ char ch;
tf=fopen("file1.dat","wb"); // открытие бинарного файла для записи
clrscr();
printf("\n Ввод товаров\n");
do
{ printf("\n название: "); scanf("%s",&t1.name);
printf(" цена: "); scanf("%f",&t1.c);
printf(" количество: "); scanf("%d",&t1.kol);
fwrite(&t1,sizeof(t1),1,tf); // запись в файл одной структуры t1
printf("\n Закончить? y/n ");
ch=getch();
}
while (ch != 'y');
fclose(tf);
}
void print(FILE *tf)
{ int i;
clrscr();
tf=fopen("file1.dat","rb"); // открытие бинарного файла для чтения
i=1;
fread(&t1,sizeof(t1),1,tf); // чтение из файла одной структуры t1
while (!feof(tf))
{ printf("\n %3d tovar %10s cena %6.2f kolic %4d",i,t1.name,t1.c,t1.kol);
fread(&t1,sizeof(t1),1,tf);
i++;
}
getch();
}
void app(FILE *tf)
{ char ch;
tf=fopen("file1. dat","ab"); // открытие бинарного файла для добавления
clrscr();
printf("\n Ввод товаров \n");
do
{ printf("\n название: "); scanf("%s",&t1.name);
printf(" цена: "); scanf("%f",&t1.c);
printf(" количество: "); scanf("%d",&t1.kol);
fwrite(&t1,sizeof(t1),1,tf);
printf(" Закончить? y/n ");
ch=getch();
}
while (ch != 'y');
fclose(tf);
}
void find(FILE *tf)
{ char c,tov[10];
long int i;
tf=fopen("file1.dat","rb+"); // открытие бинарного файла для чтения и записи
clrscr();
puts(" Название искомого товара: ");
gets(tov);
fread(&t1,sizeof(t1),1,tf);
while (!feof(tf))
{ if (strcmp(t1.name,tov)==0)
{ printf(" tovar %10s cena %6.2f kolic %d",t1.name,t1.c,t1.kol);
printf("\n изменить? y/n ");
c=getch();
if (c=='y')
{ printf("\n количество: ");scanf("%d",&t1.kol);
printf("\n цена: ");scanf("%f",&t1.c);
i=sizeof(t1);
fseek(tf,-i,1); // возврат на sizeof(t1) байт назад
fwrite(&t1,sizeof(t1),1,tf);//запись изменённой структуры
}
}
fread(&t1,sizeof(t1),1,tf);
}
fclose(tf);
}
ВЫПОЛНЕНИЕ РАБОТЫ
3.1. Проанализировать приведенную программу.
3.2. Используя функции и режим меню, создать файл из 10 записей, просмотреть файл, добавить в файл новую информацию и, применяя режим прямого доступа, выполнить задание по своему варианту.
Варианты заданий
1. Запись имеет вид: фамилия, пол, год рождения и рост. Вывести данные о самом высоком спортсмене.
2. Запись имеет вид: название вуза, число студентов, количество факультетов. Добавить в конец файла информацию о трех новых вузах и посчитать общее число студентов.
3. Запись имеет вид: название издания, газеты или журнала, стоимость одного экземпляра, количество экземпляров в год. Вывести на печать информацию о самом дешевом издании.
4. Запись имеет вид: фамилия студента, номер зачетной книжки, 4 оценки за экзамен. Выводить информацию о всех двоечниках и корректировать ее.
5. Запись имеет вид: фамилия спортсмена, его номер, количество набранных очков. Поменять местами в файле записи о первых двух спортсменах. *
6. Запись имеет вид: фамилия, номер телефона, дата рождения. Внести в начало списка информацию о четырех новых знакомых.
7. Запись имеет вид: название инструмента, число, месяц и год изготовления. Вывести на печать информацию об инструменте с самым большим сроком использования и выполнить корректировку этой записи.
8. Запись имеет вид: номер читательского билета, автор книги, название, дата заказа. Вывести на экран содержимое файла. Поменять местами первую и последнюю записи в файле.*
9. Запись имеет вид: фамилия спортсмена, его номер, количество набранных очков. Удалить из списка информацию о спортсмене с наименьшим количеством очков.
10. Запись имеет вид: фамилия, количество вещей, общий вес. Удалите из файла сведения о багаже, общий вес вещей в котором меньше, чем 10 кг.
11. Запись имеет вид: название команды, количество набранных очков, фамилии капитанов. Вывести на печать список в порядке набранных мест.
12. Запись имеет вид: марка видеомагнитофона, стоимость, количество. Напечатать информацию об имеющихся в продаже магнитофонах. При покупке их количество соответственно уменьшается. Предусмотреть удаление информации о видеомагнитофонах, количество которых равно нулю.
* При обмене введите промежуточную переменную типа структура.
КОНТРОЛЬНЫЕ ВОПРОСЫ
4.1. Режимы доступа к файлам.
4.2. Назначение функций fseek, ftell.
4.3. Можно ли взаимозаменять функции fscanf и fread; fprint и fwrite ?
4.4. Привести пример корректировки К-той записи в файле прямого доступа.
Лабораторная работа № 11
Динамические массивы
1. ЦЕЛЬ РАБОТЫ:приобретение практических навыков создания и обработки одномерных и многомерных динамических массивов.
ОСНОВНЫЕ ПОНЯТИЯ
При определении статического массива: <тип> <имя_массива> [количество_элементов] , имя_массива становится указателем на область памяти, выделяемой для размещения элементов массива. Количество элементов должно быть константой. Таким образом, размеры памяти, выделяемой под массив, заданы в определении массива. Но иногда нужно, чтобы размеры памяти были не фиксированными, а выделялись в ходе выполнения программы при решении конкретной задачи.
Формирование массивов с переменными размерами можно организовать с помощью указателей и средств для динамического выделения памяти. Эти средства описаны в файле <alloc.h>. Функции malloc() и calloc() динамически выделяют память в соответствии со значениями их параметров и возвращают адрес начала выделенного участка памяти. Тип возвращаемого значения указателя void *. Его можно преобразовать к указателю любого типа с помощью явного приведения типа. Функция free(void *) освобождает память, выделенную с помощью malloc() или calloc().
int *p;
p=(int *)malloc(size); //Указателю на целое p присваивается адрес начала выделенной области памяти размером size байт.
p=(int *)calloc(n, size); //Указателю на целое p присваивается адрес начала выделенной области памяти размером n*size байт.
free(p); //Освобождает выделенную по адресу p память. Преобразование указателя любого типа к типу void * осуществляется автоматически, так что в качестве фактического параметра можно подставить указатель любого типа без явного приведения типов.
Пример формирования одномерного динамического массива
//lab11_1
#include <stdio.h>
#include <conio.h>
#include <alloc.h>
main()
{ float *p,d;
int i,n;
printf("\n input n:");
scanf("%d",&n);
p=(float *)malloc(n*sizeof(float));
for (i=0;i<n;i++)
{printf("x[%d]=",i);
scanf("%f",&d);
p[i]=d;
}
for (i=0;i<n;i++)
{ if (i%4==0) printf("\n");
printf("\t x[%d]=%6.2f",i,p[i]);
}
free(p);
getch();
}
Доступ к участкам выделенной памяти выполняется с помощью операции индексирования: p[i].
Каждый элемент массива может быть, в свою очередь, массивом. Именно так конструируются динамические многомерные массивы. Рассмотрим алгоритм создания и обработки двумерного массива.
1. Определяем указатель на массив указателей , задающий адреса начала строк матрицы: тип **uk.
2. Вводим размеры матрицы n,m.
3. Создаём динамический массив указателей на указатели начала строк : uk=(тип **)malloc(n*sizeof(тип *));
4. В цикле выделяем память под n массивов – строк по m элементов в каждом: for (i=0;i<n;i++) uk[i]=(тип *)malloc(m*sizeof(тип));
5. Обработка массива (работа с индексированными элементами uk[i][j]).
6. В цикле освобождаем память, занятую под n массивов – строк : for (i=0;i<n;i++) free(uk[i]);
7. Освобождаем память, занятую под массив указателей : free(uk).
Пример обработки двумерного динамического массива
Составить программу, создающую динамическую матрицу размером n*n, заполнить матрицу случайными числами. Вычислить сумму каждой строки и поместить суммы строк в одномерный динамический массив.
//lab11_2
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <alloc.h>
void main()
{ int n,j,i;
float ** matr; float * mass; // Объявляем matr - указатель на массив указателей //mass – указатель на одномерный массив
clrscr();
printf("Введите размер квадратной матрицы n: "); scanf("%d",&n);
mass=(float *)malloc(n*sizeof(float )); // Выделяем память под массив mass
if (mass==NULL)
{ puts("не создан динамический массив!");
return;}
matr=(float **)malloc(sizeof(float *)*n); // Выделяем память под массив указателей
if (matr==NULL)
{ puts("не создан динамический массив!");
return;}
randomize();
for (i=0;i<n;i++)
{ matr[i]=(float *)malloc(sizeof(float)*n); // Выделяем память под i-ю строку
if (matr[i]==NULL)
{ puts("не создан динамический массив!");
return;}
for (j=0;j<n;j++) matr[i][j]=random(100);
}
for (i=0;i<n;i++)
{ mass[i]=0;
for (j=0;j<n;j++)
mass[i]+=matr[i][j];
}
for (i=0;i<n;i++)
{ for (j=0;j<n;j++)
printf("\t%6.2f",matr[i][j]);
printf("\n");
}
for (i=0;i<n;i++)
printf("\n сумма %d строки %8.2f",i,mass[i]);
for (i=0;i<n;i++)
free(matr[i]); //Освобождаем память i – й строки
free(matr); // Освобождаем память массива указателей
free(mass); // Освобождаем память массива сумм
getch();
}
ВЫПОЛНЕНИЕ РАБОТЫ
3.1. Проанализировать приведенные программы.
3.2. Создать двумерный динамический массив и выполнить задание по своему варианту.
Варианты заданий
1. Даны матрица A размером m*n и вектор В размером m. Записать на главную диагональ элементы вектора, а в вектор - элементы главной диагонали.
2. Выбрать максимальный элемент матрицы С (размер m*n), элементы четных строк разделить на максимальный элемент, а к элементам нечетных прибавить максимальный элемент.
3. Найти минимальный элемент матрицы С (размер m*n), и поменять его местами с первым элементом.
4. Дана матрица Е размером m*n. Вычислить суммы элементов каждого столбца. Определить наибольшее значение этих сумм и номер соответствующего столбца.
5. В матрице К размером m*n найти в каждом столбце произведение отрицательных элементов и количество нулевых элементов в матрице .
6. Даны две матрицы А и В одинаковой размерности m*n. Получить матрицу
C = max (a i j, b i j ), и матрицу D = min (a i j, b i j).
7. Дана матрица Р размером m*n . Найти сумму минимальных элементов каждого столбца матрицы.
8. Даны матрицы: А размером m*k и В размером k*n.Получить матрицуС=A*В.
9. Дана матрица К размером m*n. Вычислить сумму минимальных элементов каждого столбца.
10. Дана матрица С размером m*n. Упорядочить эту матрицу по возрастанию элементов в каждом столбце.
11. Дан одномерный массив A из m элементов. Вводится число k (k<m). Получить из А матрицу, по k элементов в строке. Недостающие элементы заменить 0.
12. В матрице Т размером m*k переставить элементы в строках так, чтобы по диагонали они были упорядочены по возрастанию.
КОНТРОЛЬНЫЕ ВОПРОСЫ
4.1. Отличия динамического массива от статического.
4.2. Как создать одномерный динамический массив?
4.3. Как создать динамическую матрицу?
4.4. Как освобождается память, занятая под динамические структуры?
Лабораторная работа № 12