Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Программирование на языке Си. Учебно-методическое пособие

.pdf
Скачиваний:
1
Добавлен:
07.09.2026
Размер:
1 Мб
Скачать
☆
81
Есть также аналоги функций printf и scanf для работы с файлами. fprintf(f,”формат”, значение) fscanf(f,”формат”, адрес)
Пример вывода таблицы Пифагора в файл.
#include <stdio.h> main(){ FILE*f; int i, j; f=fopen("a.txt", "w"); for (i=1; i<10; i++) { for (j=1; j<10; j++) fprintf(f,"%5d",i*j); fputs("\n",f); } fclose(f); }
Можно использовать
fscanf(stdin, ...) fprintf(stdout, ...)
Вместо файла используются стандартные потоки ввода-вывода.
переход в начало файла
rewind(f);
Пример: удалить лишние пробелы в файле.
#include <stdio.h> int main(){ FILE *f1, *f2; int i; char st[80]; f1=fopen("a.txt","r"); f2=fopen("b.txt","w"); while(fgets(st,80,f1)) { for(i=0; i<strlen(st); i++) { if(st[i]!=' ' || st[i+1]!=' ') fputc(st[i],f2); } } fclose(f2); }
Пример создания массива строк из слов, содержащихся в файле. Разбиение строки на слова.
#include <stdio.h> #include <string.h>
82
int main(){ FILE *f1; int i,j=0,n=0; char st[80], m[200][40]; f1=fopen("a.txt","r"); while(fgets(st,80,f1)) { for(i=0; i<strlen(st); i++) { if(st[i]!=' '&&st[i]!='\n') m[n][j++]=st[i]; if((st[i+1]==' ' || st[i+1]=='\n') && st[i]!=' ') {m[n][j]=0; n++; j=0;} } } fclose(f1); for(i=0;i<n;i++) puts(m[i]); }
Функция очистки буфера ввода-вывода
int fflush(FILE *)
Если f – файл, открытый для записи, то вызов fflush() приводит к физической записи содержимого буфера в файл. Если же f указывает на файл, открытый для
чтения, то очищается входной буфер. В обоих случаях файл остается
открытым.
Возврат 0 означает успех, а возврат ненулевой величины указывает на наличие
ошибки по записи. Закрытие файла также приводит к очистке буферов.
Функции для работы с файлами на диске
int remove(“name”) удаление заданного файла на внешнем носителе int rename(“oldname”, “newname”) переименование заданного файла на внешнем носителе возвращается 0 если успешно. если файлы открыты – необходимо закрыть.
Пример
fclose(f1); fclose(f2); if (remove("a.txt") == 0) printf("Removed \n"); else printf("error remove"); if (rename("a.tmp","a.txt") == 0) printf("Rename \n"); else printf("error rename");
Работа с каталогами <dir.h>
83
int mkdir(const char *path); создание каталога
int rmdir(const char *path); удаление каталога
int chdir(const char *path); смена текущего каталога 0 – если успешно
удаляется только пустой каталог.
Пример
if (!mkdir("111")) printf("Directory created\n"); else printf("Unable to create directory\n"); getch(); system("dir/p"); //вывод содержимого каталога if (!rmdir("111")) printf("Directory removed\n"); else printf("Unable remove directory\n"); getch(); system("dir/p"); getch(); chdir("d:\\"); system("dir/p");
Двоичные файлы
Чтение и запись
int fwrite(void *p, int size, int n, FILE *f); int fread(void *p, int size, int n, FILE *f);
возвращают число реально считанных или записанных элементов. р – область памяти для чтения-записи, size – размер элемента, n – количество элементов, f – файловая переменная.
Пример программы записи массива в файл, и вывода его из файла на экран.
#include <stdio.h> int main(){ FILE *f; int m[80]={65, 120, 70, 40, 10, 45, 155}; f=fopen("a.dat","wb"); fwrite(m, sizeof(int), 7, f); fclose(f); int p[80], n, i; f=fopen("a.dat","rb");
84
i=0; for( ;fread(p+i, sizeof(int), 1, f); i++) printf("%4d", p[i]); }
программа копирования файлов, использующая параметры
//debug - paramerers #include <iostream.h> #include <conio.h> int main(int argc, char *argv[]){ FILE *f1, *f2; char p[512], st1[80], st2[80]; int n, i; if(argc <=1) {cout<<"name1 "; cin>>st1;} else strcpy(st1,argv[1]); if(argc <=2) {cout<<"name2 "; cin>>st2;} else strcpy(st2,argv[2]); cout<<st1<<"->"<<st2<<endl; if((f1=fopen(st1,"rb"))==NULL) { cout<<"error "<<st1; getch(); return 1; } if((f2=fopen(st2,"wb"))==NULL) { cout<<"error "<<st2; getch(); return 2; } i=0; do { n=fread(p, 1, 512, f1); fwrite(p, 1, n, f2); i+=n; } while(n==512); fclose(f2); cout<<"copy bait "<<i<<endl; getch(); }
перемещение по файлу
int fseek(FILE *f, long n, int w);
0- если успешно
n – на сколько байт, w – откуда 0 – on начала, 1 – от тек позиции, 2 – от конца. n – может быть отрицательным.
fseek(f,0,0) – переход в начало файла fseek(f,0,2); - переход в конец файла fseek(f,-1,1); - переход к предыдущему элементу в файле
long ftell(FILE *f) возвращает текущее положение указателя в файле.
85
Пример функции определения длины файла
long filesize(FILE *f) { long curpos, length; curpos = ftell(f); fseek(f, 0, 2); length = ftell(f); fseek(f, curpos, 0); return length; }
Файл из структур – база данных
Пример записной книжки.
struct zap { char fam[15]; char name[10]; char tel[13]; }b; zap a[N]={{"petrova","ira","2665522"},{"ivanov","ivan","9139204512"}, {"sidorov","andrd","123456"}};
f1=fopen("a.dat","wb"); fwrite(a, sizeof(zap), 3, f1); fclose(f1); f2=fopen("a.dat","rb+"); while(fread(&b, sizeof(zap), 1, f2)) { cout<<b.fam<<endl; }
Пример поиска и изменения записей в БД.
fseek(f1,0,0); while(fread(&b, sizeof(zap), 1, f1)) { if (strcmp(b.fam,"petrova")==0) { fseek(f1,-sizeof(zap),1); strcpy(b.fam,"sergeeva"); fwrite(&b, sizeof(zap), 1, f1); fflush(f1); } cout<<b.fam<<endl; }; fclose(f1);
86
Удаление элементов из файла – два варианта:
1. Пометка записей как удаленных. Быстро, но место на диске не
освобождается.
2. Перезапись в другой файл. Медленно, но файл в этом случае
уменьшается.
На практике комбинируют оба метода.
Пример 1 варианта
fseek(f1,0,0); while(fread(&b, sizeof(zap), 1, f1)) { if (strcmp(b.fam,"sergeeva")==0) { fseek(f1,-sizeof(zap),1); b.fam[0]=0; fwrite(&b, sizeof(zap), 1, f1); fflush(f1); } } fseek(f1,0,0); while(fread(&b, sizeof(zap), 1, f1)) if(b.fam[0]!=0) printf("%s\n",b.fam);
Пример 2 варианта
fseek(f1,0,0); f2=fopen("b.dat","wb+"); while(fread(&b, sizeof(zap), 1, f1)) { if (strcmp(b.fam,"sidorov")!=0) fwrite(&b, sizeof(zap), 1, f2); } fclose(f1); fclose(f2); remove("a.dat"); rename("b.dat","a.dat"); f1=fopen("a.dat","rb+"); while(fread(&b, sizeof(zap), 1, f1)) if(b.fam[0]!=0) printf("%s\n",b.fam);
Сортировка элементов в файле
Если файл достаточно большой, и его элементы затруднительно считать в массив для последующей сортировки, приходиться сортировать
непосредственно элементы файла на внешнем носителе. В этом случае, чтобы минимизировать количество перемещений по файлу, можно использовать метод слияния.
87
Пример:
#include <stdio.h> #include <conio.h> #include <stdlib.h> const int N=33;
main(){ FILE *f,*f1,*f2; int i,i1,i2,n=1,k,j,n1,n2,flag; unsigned char x,y; srand(2); f=fopen("f.tmp","wb+"); for(i=0;i<N;i++) { x=rand()%256; printf("%8d",x); fwrite(&x,sizeof(x),1,f); } printf("\n");
for(n=1; n<N; n*=2) { rewind(f); f1=fopen("f1.tmp","wb+"); f2=fopen("f2.tmp","wb+"); for(i=j=0;i<N;i++){ fread(&x,sizeof(x),1,f); if(j++ < n)fwrite(&x,sizeof(x),1,f1); else fwrite(&x,sizeof(x),1,f2); if(j==n*2) j=0; } rewind(f); rewind(f1); rewind(f2); n1=fread(&x,sizeof(x),1,f1); n2=fread(&y,sizeof(x),1,f2); while(n1||n2) { for(i1=i2=i=0;i<n*2&&(n1||n2);i++) { if(x<y && i1<n && n1 || i2>=n || !n2) fwrite(&x,sizeof(x),1,f),n1=fread(&x,sizeof(x),1,f1),i1++; else fwrite(&y,sizeof(x),1,f),n2=fread(&y,sizeof(x),1,f2),i2++; } } } printf("\n"); rewind(f); while(fread(&x,sizeof(x),1,f)) printf("%8d",x); }
88
Контрольные вопросы
Какие типы файлов вы знаете? Чем отличается файл от массива? Особенности организации текстовых и двоичных файлов. Что понимается под чтением, и что под записью в файл?
12. Деревья
Дерево – это совокупность узлов и соединяющих их ветвей, причем в каждый узел (за исключением одного – корня) ведет ровно одна ветвь. Корень – это начальный узел дерева, в который не ведет ни одной ветви.
Предком для узла x называется узел дерева, из которого существует путь в узел x (потомок – наоборот).
Родителем для узла x называется узел дерева, из которого существует непосредственная ветка в узел x (сын – наоборот).
Листом дерева называется узел, не имеющий потомков.
Рассмотрим двоичное дерево. Двоичным деревом называется дерево, каждый узел которого имеет не более двух сыновей. Полным двоичным деревом называется дерево, у которого каждая внутренняя вершина имеет
непустые левое и правое поддеревья.
Опишем следующую структуру для создания дерева:
struct tree { int key; tree *l, *r; };
где tree *l, *r ссыдки на правого и левого сына данного узла.
Деревья очень удобны для поиска в них информации. Однако для
быстрого поиска требуется предварительная подготовка – дерево надо построить специальным образом. Дерево строится таким образом, что значения всех вершин левого поддерева вершины x меньше x, а значения всех вершин правого поддерева x больше или равно вершины x.
Построим дерево для последовательности 65, 120, 70, 40, 10, 45, 155
65 40 120
10 45 70 155
89
Алгоритм построения дерева
Сравниваем очередной элемент массива с ключом корня. Если новый элемент меньше, включаем его в левое поддерево, если больше или равен, то в правое. Если текущее дерево пустое, создаем новую вершину (корень) и включаем в дерево.
Удобно строить деревья с помощью рекурсии. В параметрах функции add(tree * &t, int data) ставить & обязательно, через него возвращается адрес ссылки.
Функция для добавления в дерево новых узлов.
void add (tree * &t, int data) { if ( !t ) { t = new tree; t->key = data; t->l = t->r = NULL; return; } if ( data < t->key ) add( t->l, data ); else add ( t->r, data ); }
Одной из необходимых операций при работе с деревьями является обход дерева, во время которого надо посетить каждый узел по одному разу и
(возможно) вывести информацию, содержащуюся в узлах. Пример – левое поддерево, корень, правое поддерево (ЛКП обход) – узлы будут пройдены в порядке возрастания. ПКЛ обход – узлы будут пройдены в порядке убывания.
ЛКП обход
void print_t(tree *t){ if ( !t ) return; print_t(t->l); printf("%d ", t->key); print_t(t->r); }
ПКЛ обход
void print_t(tree *t){ if ( !t ) return; print_t(t->r); printf("%d ", t->key); print_t(t->l); }
90
КЛП обход
void print_t(tree *t){ if ( !t ) return; printf("%d ", t->key); print_t(t->l); print_t(t->r); }
ЛПК обход
void print_t(tree *t){ if ( !t ) return; print_t(t->l); print_t(t->r); printf("%d ", t->key); }
Алгоритм поиск по дереву. Сначала проверяем ключ корня, если он равен искомому, то поиск заканчиваем. Если он меньше искомого, ищем в левом поддереве от узла, если больше – то в правом. Приведенная функция возвращает адрес нужной вершины, если поиск успешный, и NULL, если требуемый элемент не найден.
tree * search (tree * t, int x) { if ( ! t ) return NULL; if ( x == t->key ) return t; if ( x < t->key ) return search ( t->l, x ); return search ( t->r, x ); }
Пример программы с использованием дерева.
int main(){ tree *p=NULL, *t; int m[80]={65, 120, 70, 40, 10, 45, 155}; int i; for(i=0; i<7; i++) add(p,m[i]); print_t(p); cout<<endl;
t=search(p,45); if(t) cout<<"found "<<t->key; else cout<< "no found"; }
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]