Типичная задача
Написать программу, которая должна делать следующее: Содержание неупорядоченного файла переписать в файл, организованный разделами. Разработать функции редактирования файла и работы с ним.
#include <stdio.h>
#include <string.h>
#include <conio.h>
#include <stdlib.h>
#include <mem.h>
#include <io.h>
#define byte unsigned char
#define word unsigned int
#define dword unsigned long
//Количество строк, читаемых в память при сортировке в разделе
#define NREAD 3
struct Stroka //Тип строки
{
byte Razm; //Размер строки
char Str[40]; //Сама строка
};
struct Spravka //Структура справки
{
dword AdrNachOgl; //Адрес начала оглавления
dword AdrNachData; //Адрес начала данных
dword OglSize; //Размер оглавления (количество записей)
};
struct StrOglav //Структура элемента оглавления
{
char RName[15]; //Название раздела
dword AddrNach; //Адрес начала раздела
dword RSize; //Размер раздела
};
class IFile //Класс - lib - файлы
{
private:
FILE *f; //Реальный открываеиый файл
char *Razd; //Переменная для хранения адреса раздела,
//хранящегося в ОП
int RazdNom; //и номер этого раздела
dword AdrFree; //Адрес свободного участка
dword FreeSize; //Размер свободного участка
char filename[13]; //Имя открытого файла
Spravka Spr; //Справка
StrOglav Oglav[10]; //Оглавление
void ErrorMe(int,char *,int); //Вывод сообщения о ошибке
void DoFile(); //Создание lib файла из текстового файла
void FileInfo(); //Вывод информации о файле
void NazvOut(const char *); //Вывод названия выбранной опции
void ReadRazd(int); //Чтение раздела в память
void View_Edit(); //Просмотр и редактирование файла
void Structure(); //Вывод структуры файла
char FindFree(dword); //Поиск свободного участка
void DelRazd(); //Удаление раздела
void SaveInfo(); //Сохранить справку и оглавление
void NewRazd(); //Создать новый раздел
void Sorting(); //Отсортировать оглавление
void Inside_Sorting(); //Отсортировать содержимое оглавления
void Poisk(); //Поиск строки по разделам
public:
IFile();
void MainMenu(); //Вывод меню
~IFile();
};
////////////////////// IFILE \\\\\\\\\\\\\\\\\\\\\\\
IFile::IFile()
{
Razd=NULL;RazdNom=0;
clrscr();
NazvOut(" ОТКРЫТИЕ ФАЙЛА ");
printf("Введите имя файла (*.txt или *.lib)->");
scanf("%s",filename); //Вводим название открываемого файла
char *c=strchr(filename,'.'); //Анализируем введенную строку
if(c==NULL) //Выходим, если введена без расширения
{ErrorMe(2,filename,0);return;}
c++;
if(strcmp(c,"txt")==0) //Если с расширением .txt, создаем
DoFile(); //из txt файла lib файл
f=fopen(filename,"r+b"); //Открываем файл для записи и чтения
if(f==NULL)
{ErrorMe(0,filename,0);return;} //ОШИБКА если файл не существует
read(fileno(f),&Spr,sizeof(Spravka)); //Читаем из файла в память справку
read(fileno(f),Oglav,sizeof(StrOglav)*10);//и оглавление
};
void IFile::DoFile() //Функция, создающая из txt файла lib файл
{
FILE *tmp=fopen(filename,"rt"); //Открываем файл на чтение
if(tmp==NULL) //ОШИБКА если файла не существует
{ErrorMe(0,filename,1);return;}
strcpy(strchr(filename,'.'),".lib"); //Меняем расширение названия на lib
//не самого файла, а только названия
f=fopen(filename,"wb"); //создаем файл с таким расширением
//Вычисляем размер информационной части
int uk_in_file=sizeof(Spravka)+sizeof(StrOglav)*10;
char *p;
Stroka buf; //Заполняем поля информации
Spr.AdrNachOgl=12;
Spr.AdrNachData=12+sizeof(StrOglav)*10;
Spr.OglSize=0;
Razd=(char *)malloc(100); //Выделяем память для хранения раздела
char *uk=Razd;
//Читаем из файла первую строку
fgets(buf.Str,80,tmp);buf.Razm=strlen(buf.Str)-1;
if(buf.Str[buf.Razm]=='\n')buf.Str[buf.Razm]=0;
do
{
uk=Razd;
//Если строка начинается с +, это название нового раздела
if(buf.Str[0]!='+')
{ErrorMe(3,buf.Str,1);return;}
p=buf.Str+1;
//Создаем новый раздел
strcpy(Oglav[Spr.OglSize].RName,p);
//Читаем новую строку
fgets(buf.Str,80,tmp);buf.Razm=strlen(buf.Str)-1;
if(buf.Str[buf.Razm]=='\n')buf.Str[buf.Razm]=0;
//Читаем строки и заполняем раздел, пока не дойдем до конца или
//встретим название нового раздела
while((buf.Str[0]!='+')&&(!feof(tmp)))
{
movmem(&buf,uk,buf.Razm+1);
uk+=buf.Razm+1;
fgets(buf.Str,80,tmp);buf.Razm=strlen(buf.Str)-1;
if(buf.Str[buf.Razm]=='\n')buf.Str[buf.Razm]=0;
}
if((buf.Str[0]!='+')&&(buf.Razm>1))
{
movmem(&buf,uk,buf.Razm+1);
uk+=buf.Razm+1;
}
//Заполняем поля соотв. строки в оглавлении
Oglav[Spr.OglSize].RSize=uk-Razd;
Oglav[Spr.OglSize].AddrNach=uk_in_file;
fseek(f,uk_in_file,SEEK_SET);
write(fileno(f),Razd,uk-Razd);
uk_in_file+=uk-Razd;
Spr.OglSize++;
}while(!feof(tmp));
//Выполяем все действия, пока не дойдем до конца файла
free(Razd);Razd=NULL;
SaveInfo();
fclose(tmp);
fclose(f);
printf("Удачно создан файл: \"%s\"\n",filename);
getch();
}
void IFile::ErrorMe(int kod,char *s,int fatal)
//Вывод сообщения об ошибке с кодом kod
{
printf("ОШИБКА %d: ",kod);
switch(kod)
{
case(0):printf("не могу открыть файл \"%s\"\n",s);break;
case(1):printf("неверный номер\n");break;
case(2):printf("необходимо вводить название файла с расширением: %s\n",s);break;
case(3):printf("неверный формат текстового файла: \"%s\"\n",s);break;
default:printf("неизвестная ошибка\n");
}
if(fatal)exit(0);
getch();
}
void IFile::MainMenu() //Вывод основного меню на экран
{
char c;
do
{
clrscr();
NazvOut(" ФАЙЛЫ ОРГАНИЗОВАННЫЕ РАЗДЕЛАМИ ");
printf("[1] Информация о файле\n");
printf("[2] Просмотр и редактирование раздела\n");
printf("[3] Схематическая структура файла\n");
printf("[4] Сортировка оглавления\n");
printf("[5] Сортировка внутри раздела\n");
printf("[6] Новый раздел\n");
printf("[7] Удалить радел\n");
printf("[8] Поиск элемента\n");
printf(">");
c=getche();
if(c==0)c=getch(); //Чтение клавиши
switch(c) //В зависимости от введенного значения
{ //выполняем соотв. действия
case('1'):FileInfo();break;
case('2'):View_Edit();break;
case('3'):Structure();break;
case('4'):Sorting();break;
case('5'):Inside_Sorting();break;
case('6'):NewRazd();break;
case('7'):DelRazd();break;
case('8'):Poisk();break;
}
}while(c!=27); //По коду ESC выходим
SaveInfo(); //и сохраняем информацию
}
void IFile::FileInfo() //Вывод на экран информации о файле
{
clrscr();
NazvOut(" ИНФОРМАЦИЯ ");
printf("Файл \"%s\"\n",filename);
printf("Справка по смещению [0000]\n");
printf("Оглавление по смещению [%.4X]\n",Spr.AdrNachOgl);
printf("Данные начинаются по смещению [%.4X]\n",Spr.AdrNachData);
printf("Всего разделов: %d\n",Spr.OglSize);
if(Spr.OglSize>0)
{
printf(" Оглавление:\n");
for(int i=0;i<Spr.OglSize;i++)
{
printf("Раздел №%d:",i);
printf(" \"%s\"",Oglav[i].RName);
printf("(начинается: [%.4X]",Oglav[i].AddrNach);
printf(", размер: %.4X)\n",Oglav[i].RSize);
}
}
getch();
}
void IFile::NazvOut(const char *naz)
//Вывод названия текущих действий (вспомогательная функция)
{
int l=strlen(naz);
gotoxy((80-l)/2,wherey());
textcolor(BLACK);textbackground(LIGHTGRAY);
cprintf("%s\n\r",naz);
textcolor(LIGHTGRAY);textbackground(BLACK);
}
void IFile::ReadRazd(int nom) //Чтение в память раздела nom
{
if(nom>=Spr.OglSize)return; //Если номер превышает размер оглавления,выходим
if(Razd){free(Razd);Razd=NULL;} //Выделяем память под раздел
Razd=(char *)malloc(Oglav[nom].RSize);
fseek(f,Oglav[nom].AddrNach,SEEK_SET);//Устанавливаем указатель в файле на
//начало соотв. раздела
read(fileno(f),Razd,Oglav[nom].RSize);//Читаем его в память
RazdNom=nom;
};
void IFile::View_Edit() //Редактирование и просмотр раздела
{
clrscr();
NazvOut(" ПРОСМОТР И РЕДАКТИРОВАНИЕ РАЗДЛА ");
printf("Введите номер раздела->");
int nom;scanf("%d",&nom); //Вводим номер раздела
if(nom>=Spr.OglSize)
{ErrorMe(1,"",0);return;}
clrscr();
ReadRazd(nom);
NazvOut(" ПРОСМОТР И РЕДАКТИРОВАНИЕ РАЗДЕЛА ");
printf("Раздел \"%s\" содержит информацию:\n",Oglav[nom].RName);
//Выводим содержимое раздела
int pos=0,_pos;
Stroka s;
textcolor(WHITE);
while(pos<Oglav[nom].RSize)
{
s.Razm=Razd[pos];
_pos=pos;
pos++;
movmem(&Razd[pos],&s.Str,s.Razm);
s.Str[s.Razm]=0;
cprintf("%s\n\r",s.Str);
pos+=s.Razm;
}
textcolor(LIGHTGRAY);
printf("[+] Добавить строку [-] Удалить последнюю строку\n");
printf(">");
char c=getche();
Stroka New;
switch(c) //Удаление или добавление строки
{
case('+'):{ //Добавить строку
printf("Введите строку:\n");
scanf("%s",New.Str);
clrscr();
NazvOut(" ПРОСМОТР И РЕДАКТИРОВАНИЕ РАЗДЕЛА ");
printf("Раздел \"%s\" содержит информацию:\n",Oglav[nom].RName);
pos=0;
textcolor(WHITE);
//Вывод информации о разделе
while(pos<Oglav[nom].RSize)
{
s.Razm=Razd[pos];
pos++;
movmem(&Razd[pos],&s.Str,s.Razm);
s.Str[s.Razm]=0;
cprintf("%s\n\r",s.Str);
pos+=s.Razm;
}
cprintf("%s\n\r",New.Str);
textcolor(LIGHTGRAY);
New.Razm=strlen(New.Str);
printf("Раздел увеличился\n");
word ram=Oglav[nom].AddrNach;
Oglav[nom].AddrNach=0;
//Поиск подходящего свободного участка для нового
//увеличенного раздела
char find=FindFree(Oglav[nom].RSize+New.Razm+1);
Oglav[nom].AddrNach=ram;
if(find)
//Если свободный учсток найден
{
printf("Найдено подходящее место\n");
fseek(f,AdrFree,SEEK_SET);
//Записываем туда старую часть
write(fileno(f),Razd,Oglav[nom].RSize);
//Дописываем новую часть
write(fileno(f),&New,New.Razm+1);
//Корректируем оглавление
Oglav[nom].AddrNach=AdrFree;
Oglav[nom].RSize+=New.Razm+1;
printf("Раздел записан в свободный участок\n");
}
else//свободных участков нет
{
printf("Подходящих свободных участков нет, пишу в конец\n");
fseek(f,0,SEEK_END);//Устанавливаем указатель в конец
fgetpos(f,(long *)&Oglav[nom].AddrNach);
//Запимываем старую и новую часть
write(fileno(f),Razd,Oglav[nom].RSize);
write(fileno(f),&New,New.Razm+1);
Oglav[nom].RSize+=New.Razm+1;
}
};break;
case('-'):{//Уменьшение раздела
clrscr();
Oglav[nom].RSize=_pos;
NazvOut(" ПРОСМОТР И РЕДАКТИРОВАНИЕ РАЗДЕЛА ");
printf("Раздел \"%s\" содержит информацию:\n",Oglav[nom].RName);
pos=0;
textcolor(WHITE);
//Вывод содержимого
while(pos<Oglav[nom].RSize)
{
s.Razm=Razd[pos];
pos++;
movmem(&Razd[pos],&s.Str,s.Razm);
s.Str[s.Razm]=0;
cprintf("%s\n\r",s.Str);
pos+=s.Razm;
}
textcolor(LIGHTGRAY);
printf("Раздел уменьшился, пишу на старое место\n");
};break;
default:return;
}
getch();
}
void IFile::Structure() //Вывод структурной схемы файла
{
clrscr();
NazvOut(" СТРУКТУРА ФАЙЛА ");
printf(" ??????????????????????????????????????????????????????????????????\n");
dword l=filelength(fileno(f)),i;
for(i=0;i<l/64+1;i++)
printf(" ? ?\n");
printf(" ??????????????????????????????????????????????????????????????????\n");
int sch=0,color=1;
char empty[]=" ";
char *str,ind,maxind;
//Один байт - один символ в таблице
for(i=0;i<l;i++)
{
if(i%64==0)
gotoxy(7,i/64+3);
if(sch==0)
if(i==0)
{
textcolor(GREEN);textbackground(GREEN);//данный байт - начало справки
sch=12;str=empty;ind=0;maxind=0;
}
else if(i==12)
{
textcolor(RED);textbackground(YELLOW);//начало оглавления
sch=sizeof(StrOglav)*10;
str=empty;ind=0;maxind=0;
}
else
{
for(int j=0;(j<Spr.OglSize)&&(sch==0);j++)
if(i==Oglav[j].AddrNach)
{
textcolor(WHITE);textbackground(color);//начало одного из разделов
sch=Oglav[j].RSize;
color++;
str=Oglav[j].RName;maxind=strlen(Oglav[j].RName)-1;
ind=0;
};
if(sch==0)
{textcolor(LIGHTGRAY);textbackground(LIGHTGRAY);str=empty;ind=maxind=0;}
}
cprintf("%c",str[ind]);
if(ind++>maxind)ind=0;
if(sch)sch--;
}
textcolor(LIGHTGRAY);textbackground(BLACK);
gotoxy(1,l/64+5);
textbackground(GREEN);cprintf(" ");
textbackground(BLACK);printf(" - справка\n");
textbackground(YELLOW);cprintf(" ");
textbackground(BLACK);printf(" - оглавление\n");
textcolor(LIGHTGRAY);textbackground(BLACK);
getch();
}
char IFile::FindFree(dword razm)
//Поиск свободного участка размером не менее razm
{
dword last=sizeof(Spravka)+sizeof(StrOglav)*10;
dword i=last;
dword l=filelength(fileno(f));
while(i<l)
{
for(int j=0;j<Spr.OglSize;j++)
if(i==Oglav[j].AddrNach)
if((i-last>0)&&(i-last>=razm))
{
AdrFree=last;FreeSize=i-last;
return 1;
}
else
{
i+=Oglav[j].RSize;
last=i;
}
i++;
};
i--;
if((i-last>0)&&(i-last>=razm))
{
AdrFree=last;FreeSize=i-last;
return 1;
}
return 0;
};
void IFile::DelRazd() //Удаление раздела
{
clrscr();
NazvOut(" УДАЛЕНИЕ РАЗДЕЛА ");
printf("Введите номер раздела для удаления->");
int nom;
scanf("%d",&nom);
if(nom>=Spr.OglSize)
{ErrorMe(1,"",0);return;}
//Просто смещаем оглавление, поглощая запись о удаляемом разделе
movmem(&Oglav[nom+1],&Oglav[nom],sizeof(StrOglav)*Spr.OglSize-nom-1);
//Размер оглавления изменен
Spr.OglSize--;
printf("Раздел удален\n");
getch();
}
void IFile::NewRazd() //Создать новый раздел
{
clrscr();
if(Razd){free(Razd);Razd=NULL;}
Razd=(char *)malloc(100);
NazvOut(" СОЗДАНИЕ НОВОГО РАЗДЕЛА ");
int nom=Spr.OglSize;
Spr.OglSize++; //Количество разделов увеличилось
printf("Введите название раздела->");
scanf("%s",Oglav[nom].RName);
Oglav[nom].RSize=0;
printf("Вводите строки раздела (последняя - null):\n");
Stroka *s=new(Stroka);
int pos=0;
scanf("%s",s->Str); //Вводим содержимое раздела
s->Razm=strlen(s->Str);
while(strcmp(s->Str,"null"))
{
movmem(s,&Razd[pos],s->Razm+1);
pos+=s->Razm+1;
scanf("%s",s->Str);
s->Razm=strlen(s->Str);
};
delete(s);
Oglav[nom].RSize=pos;
printf("Пишу новый раздел\n");
if(FindFree(pos)) //Ищем подходящий свободный участок
{ //Участок найден
printf("Найден подходящий свободный участок\n");
fseek(f,AdrFree,SEEK_SET);
Oglav[nom].AddrNach=AdrFree;
write(fileno(f),Razd,pos);
}
else
{ //Участок не найден
printf("Пишу в конец файла\n");
fseek(f,0,SEEK_END);
fgetpos(f,(long *)&Oglav[nom].AddrNach);
write(fileno(f),Razd,pos);
}
getch();
}
void IFile::Sorting() //Сортировка оглавления пузырьковым методом
{
clrscr();
NazvOut(" СОРТИРОВКА ОГЛАВЛЕНИЯ ");
printf("[1] Сортировка по алфавиту\n");
printf("[2] Сортировка против алфавита\n");
printf(">");
char c=getch();
if((c!='1')&&(c!='2'))return;
int fl;
StrOglav tmp;
do
{
fl=0;
for(int i=0;i<Spr.OglSize-1;i++)
if((strcmp(Oglav[i].RName,Oglav[i+1].RName)>0)&&(c=='1')||
(strcmp(Oglav[i].RName,Oglav[i+1].RName)<0)&&(c=='2'))
{
movmem(&Oglav[i],&tmp,sizeof(StrOglav));
movmem(&Oglav[i+1],&Oglav[i],sizeof(StrOglav));
movmem(&tmp,&Oglav[i+1],sizeof(StrOglav));
fl=1;
}
}while(fl);
printf("Оглавление отсортировано\n");
getch();
}
void IFile::Inside_Sorting() //Сортировка слиянием одного из разделов
{
clrscr();
NazvOut(" СОРТИРОВКА ВНУТРИ РАЗДЕЛА ");
printf("Введите номер раздела->");
int nom;
scanf("%d",&nom);
if(nom>=Spr.OglSize)
{ErrorMe(1,"",0);getch();return;}
printf("[1] Сортировать по алфавиту\n");
printf("[2] Сортировать против алфавита\n>");
char c=getch();
if((c!='1')&&(c!='2'))return;
Stroka sss[NREAD],smeg;
FILE *tmp1=NULL;
//Два временных файла используются для данной сортировки
FILE *tmp2=NULL;char f1[6]="~tmp1",f2[6]="~tmp2";
char end=0;int ukaz=0,i,k;
fseek(f,Oglav[nom].AddrNach,SEEK_SET);
while(ukaz<Oglav[nom].RSize)
{
//Читаем NREAD строк из раздела
for(i=0;(i<NREAD)&&(ukaz<Oglav[nom].RSize);i++)
{
read(fileno(f),&sss[i].Razm,1);ukaz++;
read(fileno(f),sss[i].Str,sss[i].Razm);
sss[i].Str[sss[i].Razm]=0;
ukaz+=sss[i].Razm;
}
if(ukaz>=Oglav[nom].RSize)k=i;else k=NREAD;
do //Сортируем эти строки
{
end=0;
for(i=0;i<k-1;i++)
if((c=='1')&&(strcmp(sss[i].Str,sss[i+1].Str)>0)
||(c=='2')&&(strcmp(sss[i].Str,sss[i+1].Str)<0))
{
movmem(&sss[i],&smeg,sss[i].Razm+2);
movmem(&sss[i+1],&sss[i],sss[i+1].Razm+2);
movmem(&smeg,&sss[i+1],smeg.Razm+2);
end=1;
}
}while(end);
tmp1=fopen(f1,"rt");
if(tmp1==NULL)
{//Если это первая сортировка - просто выписываем строки в файл
tmp1=fopen(f1,"wt");
for(i=0;i<k;i++)
fprintf(tmp1,"%s\n",sss[i].Str);
fclose(tmp1);
}
else //Это не первая сортировка
{ //"Сливаем" содержимое файла и памяти в другой файл
i=0;
tmp2=fopen(f2,"wt");
fgets(smeg.Str,40,tmp1);smeg.Str[strlen(smeg.Str)-1]=0;
while(!((feof(tmp1)&&(i==k))))
{
if(i==k)
{
fprintf(tmp2,"%s\n",smeg.Str);
fgets(smeg.Str,40,tmp1);smeg.Str[strlen(smeg.Str)-1]=0;
}
else
if(feof(tmp1))
{
fprintf(tmp2,"%s\n",sss[i].Str);
i++;
}
else
if((c=='1')&&(strcmp(sss[i].Str,smeg.Str)>0)
||(c=='2')&&(strcmp(sss[i].Str,smeg.Str)<0))
{
fprintf(tmp2,"%s\n",smeg.Str);
fgets(smeg.Str,40,tmp1);smeg.Str[strlen(smeg.Str)-1]=0;
}
else
{
fprintf(tmp2,"%s\n",sss[i].Str);
i++;
}
}
fclose(tmp1);fclose(tmp2);
//Меняем местами файлы
char c=f1[4];f1[4]=f2[4];f2[4]=c;
}
}
tmp1=fopen(f1,"rt");
fseek(f,Oglav[nom].AddrNach,SEEK_SET);
fgets(smeg.Str,40,tmp1);smeg.Razm=strlen(smeg.Str)-1;
while(!feof(tmp1))
{
write(fileno(f),&smeg,smeg.Razm+1);
fgets(smeg.Str,40,tmp1);smeg.Razm=strlen(smeg.Str)-1;
}
fclose(tmp1);
remove(f1);remove(f2);
printf("Отсортирован.");
getch();
}
void IFile::Poisk() //Поиск элемента в разделах
{
int pos,i;char finded=0,nom=0;
char a,b;
clrscr();
NazvOut(" ПОИСК ЭЛЕМЕНТА ");
printf("Введите элемент для поиска->");
Stroka s,s1;
scanf("%s",s.Str);s.Razm=strlen(s.Str);
printf("[1] Искать внутри одного раздела\n");
printf("[2] Искать во всех разделах\n>");
pos=getche();printf("\n");
if((pos!='1')&&(pos!='2'))return;
if(pos=='1')
{printf("Введите его номер->");scanf("%d",&a);b=a+1;}
else
{a=0;b=Spr.OglSize;}
//Ищем элемент во всех разделах, начиная с a и кончая b-1
for(i=a;(i<b)&&(!finded);i++)
{
ReadRazd(i);
nom=0;
pos=0;
while((pos<Oglav[i].RSize)&&(!finded))
{
s1.Razm=Razd[pos];
if(s1.Razm==s.Razm)
{
movmem(&Razd[pos+1],s1.Str,s1.Razm);s1.Str[s1.Razm]=0;
if(strcmp(s.Str,s1.Str)==0)
finded=1;
}
pos+=s1.Razm+1;nom++;
}
}
if(finded)
{
printf("Элемент найден в разделе \"%s\" %d по счету\n",
Oglav[RazdNom].RName,nom);
}
else
printf("Элемент не найден\n");
getch();
}
void IFile::SaveInfo() //Сохраняем в файл справку и оглавление
{
fseek(f,0,SEEK_SET);
write(fileno(f),&Spr,sizeof(Spr));
write(fileno(f),Oglav,sizeof(StrOglav)*10);
}
IFile::~IFile() //Освобождаем память и закрываем файл
{
if(Razd){free(Razd);Razd=NULL;}
fclose(f);
}
void main()
{
IFile mf;
mf.MainMenu();
}