// C-Programmierung Beleg 6 // Copyright: Matthias Jauernig, 2005 #include #include #include #include // WICHTIG!: laut C9X-Standard ist fflush() nur auf Output-Streams anwendbar, // fflush(stdin) z.B. undefiniert - dass dies bei Solaris funktioniert, ist reiner Zufall, // bei Linux und anderen Systemen kann das schon ganz anders aussehen... // => daher: eigenes Fflush() als Makro realisieren, welches stdin flush'ed #define Fflush() while(getchar() != '\n') /* --- Datenstruktur -------------------------------------------------------------------------- */ typedef struct { char name[30], vorname[20]; char adresse[50]; } datensatz; typedef struct { //Datenstruktur für Indexfile char name[30]; long position; } iindex; typedef struct ELEMENT { //Datenstruktur für verkettete Liste struct ELEMENT *next; iindex *ind; } element; /* --- Funktionsdeklarationen ----------------------------------------------------------------- */ // Return-Codes der Funktionen, die char zurück geben // retval=0: kein Fehler // retval=1: Rückgabe mit Fehler ohne Fehlermeldung oder Abbruch durch Benutzer // retval=2: Rückgabe mit Fehler, zu dem Fehlermeldung ausgegeben werden kann char eingabeDatenfile(char*); char ausgabeDatenfile(char*); char aendernDatenfile(char*); element *sorteinfuegen(element*, element*); //Einfügen in sort. verk. Liste void sequAusgabeListe(element*); //verk. Liste sequentiell ausgeben void loescheListe(element*); //verk. Liste löschen char erstelleIndexfile(char*, char**); //Indexfile erstellen char sortAusgabeDatenfile(char*, char*); //sortierte Ausgabe des Datenfiles mit Hilfe des Indexfiles char pruefeExistenzDatenfile(int,char**,char**); //prüfe ob Datenfile vorhanden ist char pruefeExistenzIndexfile(char*, char); //prüfe ob Indexfile vorhanden ist /* --- main() --------------------------------------------------------------------------------- */ int main(int argc, char **argv){ //argv[1] ist evtl. filename char abfrage, *datenfile, *indexfile=NULL; char retval; if(pruefeExistenzDatenfile(argc,argv,&datenfile)!=0) return 1; do{ printf("\n-------------\n" "| Hauptmenu |\n" "-------------\n"); printf("(1) Eingabe eines Datensatzes\n" "(2) Sequentielle Ausgabe des Datenfiles\n" "(3) Datensatz aendern\n" "(4) Indexfile erstellen\n" "(5) Sortierte Ausgabe mit Indexfile\n" "(6) Verlassen\n" "> "); abfrage=getchar(); Fflush(); retval=0; switch(abfrage){ case '1': retval=eingabeDatenfile(datenfile); break; case '2': retval=ausgabeDatenfile(datenfile); break; case '3': retval=aendernDatenfile(datenfile); break; case '4': retval=erstelleIndexfile(datenfile,&indexfile); break; case '5': retval=sortAusgabeDatenfile(datenfile,indexfile); break; case '6': break; default: printf("Ungueltige Nummer!\n"); } if(indexfile && retval==0 && (abfrage=='1' || abfrage=='3')) retval=erstelleIndexfile(datenfile,&indexfile); if(retval==2) perror("Fehler"); }while(abfrage!='6'); return 0; } /* --- Funktionsdefinitionen ------------------------------------------------------------------ */ char eingabeDatenfile(char *filename){ datensatz neu; FILE *fp=fopen(filename, "ab"); //Schreiben im Append-Modus if(!fp) return 2; printf("Name eingeben: "); fgets(neu.name,30,stdin); neu.name[strlen(neu.name)-1]='\0'; printf("Vorname eingeben: "); fgets(neu.vorname,20,stdin); neu.vorname[strlen(neu.vorname)-1]='\0'; printf("Adresse eingeben: "); fgets(neu.adresse,50,stdin); neu.adresse[strlen(neu.adresse)-1]='\0'; if(!fwrite(&neu,sizeof(neu),1,fp)){ fclose(fp); return 2; } fclose(fp); return 0; } char ausgabeDatenfile(char *filename){ int i=1; datensatz neu; FILE *fp=fopen(filename, "rb"); //Lesen im Lese-Modus if(!fp) return 2; while(fread(&neu,sizeof(neu),1,fp)){ printf("\nDatensatz Nr. %d:\n",i++); printf("Name: %s\n",neu.name); printf("Vorname: %s\n",neu.vorname); printf("Adresse: %s\n",neu.adresse); } if(ferror(fp)){ fclose(fp); return 2; } fclose(fp); return 0; } char aendernDatenfile(char *filename){ int i, nr; char abfrage, zk[50]; datensatz neu; FILE *fp=fopen(filename, "rb+"); //R/W im Update-Modus if(!fp) return 2; printf("\nNr. des zu aendernden Datensatzes?: "); scanf("%d",&nr); Fflush(); if(fseek(fp,0,SEEK_END)==-1){ //Dateiende lesen fclose(fp); return 2; } i=ftell(fp); //Bytes bis zur akt. Filepos if(i/sizeof(datensatz) Soll dieser Datensatz wirklich geaendert werden (j/n)?: "); abfrage=getchar(); Fflush(); if(abfrage!='j' && abfrage!='J'){ fclose(fp); return 1; } printf("Name eingeben: "); fgets(zk,30,stdin); zk[strlen(zk)-1]='\0'; if(strcmp(zk,"")!=0) strncpy(neu.name,zk,30); printf("Vorname eingeben: "); fgets(zk,20,stdin); zk[strlen(zk)-1]='\0'; if(strcmp(zk,"")!=0) strncpy(neu.vorname,zk,20); printf("Adresse eingeben: "); fgets(zk,50,stdin); zk[strlen(zk)-1]='\0'; if(strcmp(zk,"")!=0) strncpy(neu.adresse,zk,50); if(fseek(fp,-sizeof(neu),SEEK_CUR)==-1){ //einen Datensatz zurück spulen fclose(fp); return 2; } if(!fwrite(&neu,sizeof(neu),1,fp)){ fclose(fp); return 2; } fclose(fp); return 0; } char pruefeExistenzDatenfile(int argc, char **argv, char **datenfile){ FILE *fp; char abfrage='j'; do{ if(argc==1 || (abfrage!='j' && abfrage!='J')){ *datenfile=(char*)malloc(100); printf("\nEinzulesende Datei angeben: "); scanf("%s", *datenfile); Fflush(); } else *datenfile=argv[1]; fp=fopen(*datenfile,"rb"); //File existent? if(!fp){ //Datei existiert nicht perror("Fehler"); printf("Datei neu anlegen (j/n, q=quit)?: "); abfrage=getchar(); Fflush(); if(abfrage=='J' || abfrage=='j') fp=fopen(*datenfile,"ab"); //File anlegen (Append-Modus) else if(abfrage=='Q' || abfrage=='q') return 1; //Quit else if(*datenfile!=argv[1]) free(*datenfile); } else abfrage='j'; //Datei existiert, daher weiter }while(abfrage!='j' && abfrage!='J'); fclose(fp); return 0; } char pruefeExistenzIndexfile(char *indexfile, char overwrite){ FILE *fp; char abfrage; //prüfen ob Indexfile existiert fp=fopen(indexfile,"rb"); if(!fp){ //Datei existiert nicht printf("Indexfile existiert nicht. Neu anlegen (j/n)?: "); abfrage=getchar(); Fflush(); if(abfrage=='J' || abfrage=='j'){ fp=fopen(indexfile,"ab"); //File anlegen (Append-Modus) if(!fp) return 2; else fclose(fp); }else return 1; //Rückgabe }else{ if(!overwrite){ printf("Datei existiert bereits - ueberschreiben (j/n)?: "); abfrage=getchar(); Fflush(); if(abfrage=='n' || abfrage=='N') return 1; } fclose(fp); fp=fopen(indexfile,"wb"); if(!fp) return 2; else fclose(fp); } return 0; } char erstelleIndexfile(char *datenfile, char **indexfile){ FILE *fp; //Filepointer iindex *ind; //Index für Indexfile datensatz neu; //Datensatz aus Datenfile element *akt, *kopf=NULL; //verkettete Liste char overwrite=0, retval; if(*indexfile==NULL){ *indexfile=(char*)malloc(100); printf("Indexfile angeben: "); scanf("%s",*indexfile); Fflush(); }else overwrite=1; if((retval=pruefeExistenzIndexfile(*indexfile, overwrite))!=0){ free(*indexfile); *indexfile=NULL; return retval; } //Daten aus Datenfile in verkettete Liste einlesen fp=fopen(datenfile, "rb"); //Öffnen des Datenfiles if(!fp){ fclose(fp); return 2; } while(fread(&neu,sizeof(neu),1,fp)){ //Lesen des Datenfiles ind=(iindex*)malloc(sizeof(iindex)); //Platz für ein Indexelement reservieren akt=(element*)malloc(sizeof(element)); //neues Listenelement erzeugen akt->ind=ind; //neues Indexelement mit Liste verketten strncpy(ind->name,neu.name,30); //Name auf Zk kopieren ind->position=ftell(fp)-sizeof(neu); //Position in Element für verkettete Liste schreiben kopf=sorteinfuegen(akt,kopf); //sortiertes Einfügen des gelesenen Elements in die Liste } if(ferror(fp)){ //Lesefehler aufgetreten fclose(fp); loescheListe(kopf); return 2; } fclose(fp); printf("\nIndexfile erstellt:\n"); sequAusgabeListe(kopf); //Daten aus verketteter Liste in Indexfile schreiben fp=fopen(*indexfile,"wb"); akt=kopf; while(akt){ //lesen bis Ende der verk. Liste erreicht fwrite(akt->ind,sizeof(iindex),1,fp); //Schreibe in Indexfile akt=akt->next; //Element der verk. Liste weitersetzen } loescheListe(kopf); if(ferror(fp)){ //Schreibfehler aufgetreten fclose(fp); return 2; } fclose(fp); return 0; } char sortAusgabeDatenfile(char *datenfile, char *indexfile){ iindex ind; //Index für Indexfile datensatz neu; //Datensatz aus Datenfile FILE *fp, *fp1; //Filepointer if(indexfile==NULL){ printf("Noch kein Indexfile erstellt!\n"); return 1; } //nun werden die Datensätze aus dem Datenfile mit Hilfe des Indexfiles ausgegeben printf("Ausgabe sortiert nach Name:\n" "---------------------------\n"); fp=fopen(datenfile,"rb"); if(!fp) return 2; fp1=fopen(indexfile,"rb"); if(!fp1){ fclose(fp); return 2; } while(fread(&ind,sizeof(ind),1,fp1)){ fseek(fp,ind.position,SEEK_SET); fread(&neu,sizeof(neu),1,fp); if(ferror(fp)){ //Schreibfehler aufgetreten fclose(fp); fclose(fp1); return 2; } printf("\nDatensatz Nr. %d:\n",(int)(ind.position/sizeof(datensatz))+1); printf("Name: %s\n",neu.name); printf("Vorname: %s\n",neu.vorname); printf("Adresse: %s\n",neu.adresse); } if(ferror(fp1)){ //Schreibfehler aufgetreten fclose(fp); fclose(fp1); return 2; } fclose(fp); fclose(fp1); return 0; } element *sorteinfuegen(element *tmp, element *kopf){ element *retp=kopf; //rück zu gebenden Zeiger auf Kopf setzen //kein Element oder das erste größer dem einzufügenden Element -> neuer Kopf if(kopf==NULL || strcmp(tmp->ind->name,kopf->ind->name)<0){ tmp->next=kopf; //next-Zeiger aus bisher erstes bzw. NULL retp=tmp; //rück zu gebender Zeiger=neuer Kopf }else{ //wenn noch Elemente vorh. oder kleiner einzufügendem while(kopf->next && strcmp(tmp->ind->name,kopf->next->ind->name)>0) kopf=kopf->next; //setze Zeiger weiter tmp->next=kopf->next; //zeige auf nächstes Element der Liste kopf->next=tmp; //nächstes Listenelement=neues Element } return retp; //neuen/alten Kopf zurückgeben } void sequAusgabeListe(element *akt){ while(akt){ //solange noch Elemente auszugeben printf(" %s, %ld\n",akt->ind->name,akt->ind->position); akt=akt->next; //setze Zeiger weiter } printf("\n"); } void loescheListe(element *akt){ element *tmp; while(akt){ tmp=akt; akt=akt->next; free(tmp->ind); free(tmp); } }