17.3 Verzeichnisfunktionen 

Bei den bisherigen ANSI-C-Funktionen konnte es Ihnen egal sein, wie ein Dateisystem aufgebaut ist. Es gibt zwar einige systemabhängige Faktoren, die zu beachten sind (beispielsweise das Trennzeichen von Verzeichnisnamen), aber meistens sind diese Funktionen so universell implementiert, dass es dennoch nicht zu Problemen kommt. Bei einem Zugriff auf Verzeichnisse ist es leider nicht mehr so einfach. Hierbei werden meist POSIX-konforme Funktionen verwendet, die vorwiegend in der UNIX-Welt beheimatet sind. Keine Sorge, auch MS-Windows-Anwender können diese Funktionen nutzen. In vielen Compilern unter diesem System sind diese Funktionen integriert.
17.3.1 Verzeichnis erstellen, löschen und wechseln – »mkdir()«, »rmdir« und »chdir« 

#include <sys/types.h> /* Linux/UNIX */ #include <sys/stat.h> /* Linux/UNIX */ #include <dir.h> /* MS-DOS/WIN */ int mkdir(const char *pfad, [int modus]);
Mit der Funktion mkdir() wird ein neues Verzeichnis mit dem Namen pfad angelegt. Zusätzlich werden in dem neuen Verzeichnis automatisch auch das Arbeitsverzeichnis (Working Directory) (.) und das Eltern-Verzeichnis (Parent Directory) (..) mit angelegt. Die Zugriffsrechte können über modus vergeben werden. Dies gilt aber nur für Linux/UNIX und nicht für Windows/MS-DOS. Die Modi unter Linux/UNIX entnehmen Sie bitte der Manpage von chmod().
Hierzu sehen Sie als Beispiel ein Listing, mit dem ein neues Verzeichnis erstellt wird.
/* create_dir.c */ #ifdef __unix__ #include <sys/types.h> #include <sys/stat.h> #define MODUS ,0711) #elif __WIN32__ || _MS_DOS_ #include <dir.h> #define MODUS ) #else #include <direct.h> /* Visual C++ */ #define MODUS ) #endif #include <stdio.h> #include <stdlib.h> int main(void) { char pfadname[200]; printf("Wie soll der neue Ordner heissen: "); scanf("%199s",pfadname); if(mkdir(pfadname MODUS == -1) /*Nicht schön, aber portabler*/ printf("Konnte kein neues Verzeichnis erstellen\n"); else printf("Neues Verzeichnis namens %s erstellt\n",pfadname); return EXIT_SUCCESS; }
Wurde das Programm ausgeführt, sollte sich im benannten Verzeichnis ein neuer Ordner mit dem eingegebenen Namen befinden. Unter Linux/UNIX muss außerdem beachtet werden, dass für den modus auch die Ausführrechte (Execute-Bits) gesetzt sind, um auch Zugriff auf das neue Verzeichnis zu haben.
Sofern versucht wird, ein Verzeichnis zu erstellen, das bereits existiert, wird dies fehlschlagen. errno wird dann auf einen entsprechenden Wert gesetzt (EEXIST).
Hinweis |
Es soll hierbei nicht unerwähnt bleiben, dass Sie unter Linux/UNIX nicht einfach die Zugriffsrechte für ein neues Verzeichnis bzw. eine neue Datei vergeben können, wie es Ihnen gerade passt. Sie sind dabei von einer gewissen Bit-Einschränkungsmaske abhängig, die Sie allerdings mit der Funktion umask() verändern können. Dies ist allerdings sehr systemspezifisch. Daher möchte ich Sie auf mein anderes Buch »Linux-UNIX-Programmierung« hinweisen, das Sie auch zum Probelesen auf meiner Homepage vorfinden. |
Als Nächstes soll in das eben erstellte Verzeichnis gewechselt werden. Dies gelingt mit der Funktion chdir(). Die Syntax von chdir() sieht so aus:
#include <unistd.h> /* Linux/UNIX */ #include <dir.h> /* MS-DOS/WIN */ int chdir(const char *pfad);
Mit chdir() wird in das Arbeitsverzeichnis gewechselt, das jedes ablaufende Programm besitzt. Bei einem Fehler gibt diese Funktion –1 zurück, ansonsten 0. In dem folgenden Listing wird erst ein neues Verzeichnis erstellt, und danach wird mit chdir() in das erstellte Verzeichnis gewechselt und darin eine Textdatei erzeugt.
/* change_dir.c */ #ifdef __linux__ #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #define MODUS ,0711) #elif _WIN32__ || _MS_DOS_ #include <dir.h> #define MODUS ) #else #include <direct.h> #define MODUS ) #endif #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> int main(void) { char pfadname[200]; printf("Wie soll der neue Ordner heissen : "); scanf("%199s",pfadname); if(mkdir(pfadname MODUS == -1) printf("Konnte kein neues Verzeichnis erstellen\n"); else { printf("Neues Verzeichnis namens %s erstellt\n", pfadname); printf(" --> (%s)\n", strerror(errno)); } /* Jetzt wollen wir in das neue Verzeichnis wechseln. */ if(chdir(pfadname) == -1) { printf("Konnte nicht in das Verzeichnis wechseln\n"); return EXIT_FAILURE; } else printf("Erfolgreich nach %s gewechselt!\n", pfadname); /* testfile im Verzeichnis erstellen*/ fopen("testfile", "w"); return EXIT_SUCCESS; }
Jetzt sollte sich in dem eben erzeugten Verzeichnis eine Datei namens testfile befinden. Es dürfte Ihnen aufgefallen sein, dass das Programm, wenn es sich beendet, automatisch wieder in das Verzeichnis des Elternprozesses zurückwechselt.
Wenn Sie mehrmals in einem Programm Verzeichnisse erstellen müssen und in diese wechseln, schreiben Sie besser eine Funktion wie z. B.:
int makedir(char *dir) { if(mkdir(dir, 0755) != -1) /* Windows/MS-DOS ohne 0755 */ if(chdir(dir) != -1) return OK; return ERROR; }
Wollen Sie ein Verzeichnis wieder löschen, können Sie die Funktion rmdir() verwenden. Die Syntax von rmdir() lautet:
#include <unistd.h> /* UNIX/Linux */ #include <dir.h> /* MS-DOS */ int rmdir(const char *pfad);
Mit rmdir() kann ein Verzeichnis (rmdir steht für remove directory) gelöscht werden. Unter Linux/UNIX setzt dies allerdings voraus, dass dieses Verzeichnis außer dem (.) und (..) keinen anderen Eintrag mehr beinhaltet. Bei Erfolg gibt diese Funktion 0 zurück und bei einem Fehler –1.
Dazu soll das Programm, das eben verwendet wurde, erweitert werden. Das Verzeichnis, das erstellt wurde, in das gewechselt wurde und in dem eine Datei erzeugt wurde, soll am Ende des Programms wieder gelöscht werden. Hier sehen Sie das Listing dazu:
/* remove_dir.c */ #ifdef __linux__ #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #define MODUS ,0711) #elif _WIN32__ || _MS_DOS_ #include <dir.h> #define MODUS ) #else #include <direct.h> #define MODUS ) #endif #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> int makedir(char *dir) { if(mkdir(dir MODUS != -1) if(chdir(dir) != -1) return 0; return -1; } int main(void) { char pfadname[200]; printf("Wie soll der neue Ordner heissen : "); scanf("%199s",pfadname); if(makedir(pfadname) == -1) { printf("Konnte kein neues Verzeichnis erstellen\n"); printf(" --> (%s)\n", strerror(errno)); } /* testfile im Verzeichnis erstellen */ fopen("testfile","w"); if(rmdir(pfadname) == -1) { printf("Konnte Verzeichnis %s nicht loeschen!!\n",pfadname); printf(" --> (%s)\n", strerror(errno)); } return EXIT_SUCCESS; }
Unter MS-DOS/Windows wird das Listing problemlos funktionieren. Mit Linux/UNIX kann das Verzeichnis nicht gelöscht werden, da sich dort noch eine Datei befindet. Das Verzeichnis muss also zuvor leer sein. Das vollständige Verzeichnis lässt sich mit folgendem Shell-Aufruf leeren:
rmdir Verzeichnis | rm -rf Verzeichnis
Im Listing kann dieser Aufruf folgendermaßen eingesetzt werden:
/* remove_dir_unix.c */ #include <sys/types.h> #include <sys/stat.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> int main(void) { char pfadname[200]; char deletefiles[200]; printf("Welchen Ordner wollen Sie löschen : "); scanf("%189s",pfadname); strcpy(deletefiles,"rm -rf "); strcat(deletefiles,pfadname); strcat(deletefiles,"/*"); printf("%s\n",deletefiles); system(deletefiles); if(rmdir(pfadname) == -1) { printf("Konnte Verzeichnis %s nicht löschen!!\n",pfadname); printf(" --> (%s)\n", strerror(errno)); } return EXIT_SUCCESS; }
17.3.2 In das Arbeitsverzeichnis wechseln – »getcwd()« 

Mit der Funktion getcwd() lässt sich der Name des Arbeitsverzeichnisses (Working Directory) ermitteln. Die Syntax von getcwd() lautet:
#include <unistd.h> /* Linux/UNIX */ #include <dir.h> /* MS-DOS/WIN */ char *getcwd(char *puffer, int puffergroesse);
Die Funktion schreibt in die Speicheradresse puffer den Pfadnamen des Arbeitsverzeichnisses mit abschließendem '\0'. Mit puffergroesse wird die Größe des Puffers angegeben. Die Funktion gibt bei Erfolg den Pfadnamen des Arbeitsverzeichnisses an puffer zurück oder bei einem Fehler NULL. Hier sehen Sie ein Beispiel dafür, wie diese Funktion verwendet wird:
/* working_D.c */ #ifdef __unix__ #include <unistd.h> #elif __WIN32__ || _MS_DOS_ #include <dir.h> #else #include <direct.h> /* Visual C++ */ #endif #include <stdio.h> #include <stdlib.h> int main(void) { char puffer[200]; if(getcwd(puffer,sizeof(puffer)) == NULL) { fprintf(stderr, "Fehler bei getcwd ...\n"); return EXIT_FAILURE; } printf("Working-Directory: %s\n", puffer); return EXIT_SUCCESS; }
Für Linux/UNIX gilt außerdem: Wechseln Sie in ein Verzeichnis, das ein symbolischer Link auf ein anderes Verzeichnis ist, so wird in das Verzeichnis gewechselt, auf das der symbolische Link zeigt.
Ein praktisches Beispiel unter Linux: Der User hat den Namen seines Home-Verzeichnisses vergessen. Er muss aber jetzt wieder in das Verzeichnis wechseln. Welches das ist, kann er mit der Eingabe des Shellbefehls env (Environment) oder mit der C-Funktion getenv()herausfinden. Hier sehen Sie das Listing:
/* go_home.c */ #ifdef __unix__ #include <unistd.h> #elif __WIN32__ || _MS_DOS_ #include <dir.h> #else #include <direct.h> /* Visual C++ */ #endif #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX 200 int main(void) { char puffer[MAX]; char home[MAX]; if( getenv("HOME") == NULL ) { printf("getenv(\"HOME\") findet nichts\n"); return EXIT_FAILURE; } /* das Heimatverzeichnis nach home */ strncpy(home, getenv("HOME"), MAX-1); home[MAX-1] = '\0'; /* Working Directory lesen */ if(getcwd(puffer,sizeof(puffer)) == NULL) { fprintf(stderr, "Fehler bei getcwd ...\n"); return EXIT_FAILURE; } /* Sind wir schon im Heimatverzeichnis? */ if(strcmp(home,puffer) == 0) printf("Wir sind daheim : %s\n",puffer); else { /* Nicht, dann wechseln wir ins Heimatverzeichnis. */ chdir(home); /* Der Beweis: */ printf("back at home: %s \n", getcwd(puffer,sizeof(puffer))); } return EXIT_SUCCESS; }
17.3.3 Verzeichnisse öffnen, lesen und schließen – »opendir()«, »readdir()« und »closedir()« 

Um Verzeichnisse zu lesen, ist in der Headerdatei mit #include <dirent.h> eine interne Struktur namens DIR deklariert. Der Inhalt dieser Struktur ist hier jetzt nicht von Interesse, sondern es geht um die folgenden Funktionen, die mit der Struktur arbeiten.
Hinweis |
Die folgenden Funktionen sind leider nicht mit dem Microsoft Visual-C++-Compiler ausführbar. Dafür wird aber am Ende des Kapitels ein extra Listing angefertigt, das zeigt, wie auch mit dem Visual-C++-Compiler Programme erstellt werden können, die ein Verzeichnis auslesen. |
Tipp |
Wollen Sie die folgenden Beispiele mit der kostenlosen Entwicklungsumgebung Bloodshed Dev-C++ durchführen, müssen Sie im Menü über Projekt • Projektoptionen in der Liste Linker die Bibliothek -lmingwex eintragen. Eventuell kann dies aber auch über das Menü Werkzeuge • Compiler • Optionen in die Liste Linker eingetragen werden. Hierfür genügt aber dann folgender Eintrag: mingwex |
»opendir()« – ein Verzeichnis öffnen
Wir beginnen mit der Funktion opendir():
#include <sys/types.h> #include <dirent.h> DIR *opendir(const char *dirname);
Bei Erfolg wird mit dieser Funktion das Verzeichnis dirname geöffnet, auf dessen Adresse dann der DIR-Zeiger verweist. Ansonsten wird bei einem Fehler NULL zurückgegeben.
Der DIR-Zeiger wird jetzt verwendet, um den Inhalt eines Verzeichnisses auszulesen. Dies wird jetzt gleich mit der Funktion readdir() vorgenommen.
»readdir()« – aus einem Verzeichnis lesen
Die Syntax von readdir() lautet:
#include <sys/types.h> #include <dirent.h> struct dirent *readdir(DIR *dir);
Bei einem Fehler gibt diese Funktion ebenfalls NULL zurück. Ansonsten gibt sie eine Adresse der Struktur dirent zurück, die Folgendes beinhaltet:
struct dirent { long d_ino; /* i-node Nr. (bei Windows/MS-DOS immer 0) */ unsigned short d_reclen; /* (bei Windows/MS-DOS immer 0) */ unsigned short d_namlen; /* Länge des Namens in d_name */ char *d_name; /* Dateiname mit abschließendem '\0' */ };
In der Praxis kann die Funktion readdir() so verwendet werden:
DIR *dir; struct dirent *dirzeiger; /* Verzeichnis öffnen */ if((dir=opendir(dirname)) != NULL) /* komplettes Verzeichnis Eintrag für Eintrag auslesen */ while((dirzeiger=readdir(dir)) != NULL) printf("%s\n",(*dirzeiger).d_name);
Es wird zuerst mit opendir() ein Verzeichnis geöffnet und danach mit readdir() der komplette Inhalt des Verzeichnisses ausgegeben.
»rewinddir()« – Verzeichnis-Zeiger auf den Anfang zurücksetzen
Mit der Funktion rewinddir() wird der Lesezeiger wieder an den Anfang der Namensliste des Verzeichnisses zurückgesetzt. Die Syntax von rewinddir() lautet:
#include <sys/types.h> #include <dirent.h> void rewinddir(DIR *dir);
»closedir()« – Verzeichnis schließen
Am Ende wird dann mit der Funktion closedir() das Verzeichnis geschlossen, das mit opendir() geöffnet wurde. Bei Erfolg gibt diese Funktion 0 und bei Fehler –1 zurück. Die Syntax lautet:
#include <sys/types.h> #inlcude <dirent.h> int closedir(DIR *dir);
Hier folgt ein ausführbares Beispiel, das alle Funktionen in Aktion demonstriert:
/* read_dir.c */ #include <sys/types.h> #include <dirent.h> #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { DIR *dir; struct dirent *dirzeiger; if(argc != 2) { fprintf(stderr,"Benutzung : %s Directory\n", argv[0]); return EXIT_FAILURE; } /* das Verzeichnis öffnen */ if((dir=opendir(argv[1])) == NULL) { fprintf(stderr,"Fehler bei opendir ...\n"); return EXIT_FAILURE; } /* das komplette Verzeichnis auslesen */ while((dirzeiger=readdir(dir)) != NULL) printf("%s\n",(*dirzeiger).d_name); /* Lesezeiger wieder schließen */ if(closedir(dir) == -1) printf("Fehler beim Schließen von %s\n", argv[1]); return EXIT_SUCCESS; }
Mit diesem Programm wird das vollständige Verzeichnis ausgegeben, das Sie über die Kommandozeile angeben.
»telldir()« und »seekdir()« – Positionierung im Verzeichnis
Auf einigen Systemen gibt es zusätzlich noch die Funktion telldir():
#include <sys/types.h> #include <sys/dirent.h> off_t telldir(DIR *dirptr)
Diese Funktion liefert zu einem mit readdir() gelesenen Verzeichnis die Position des Lesezeigers zurück.
Mit der Funktion seekdir() lässt sich die Position des DIR-Zeigers verschieben:
#include <sys/types.h> #include <sys/dirent.h> void seekdir(DIR *dirptr, off_t pos)
Damit wird der mit opendir() geöffnete Lesezeiger (dirptr) auf die Position pos gesetzt, die Sie zuvor mit der Funktion telldir() ermittelt haben. Hierzu noch ein kurzer Ausschnitt dieser beiden Funktionen:
off_t pos; /* aktuelle Position im Verzeichnis ermitteln */ pos = telldir(dir_ptr); /* viele Funktionen */ ... /* zur aktuellen Position zurückspringen */ seekdir(dir_ptr, pos);
Probleme mit der Portabilität
Ein Problem bei Funktionen wie opendir(), readdir() oder closedir() ist, dass sie POSIX-konform und aus diesem Grund häufig nicht bei Compilern für MS-Windows implementiert sind. Unter UNIX-artigen Systemen müssen Sie sich wegen dieser Funktionen keine Gedanken machen. Um also unter MS-Windows, genauer gesagt unter WIN32, ein vollständiges Verzeichnis auszugeben, müssen Sie auf die Windows-Systemprogrammierung zurückgreifen. Die Windows-Programmierung hier genauer zu erläutern, würde den Rahmen des Kapitels oder gar des Buchs sprengen. Aber zu Anschauungszwecken folgt hier eine portablere Lösung, mit der Sie ein vollständiges Verzeichnis ausgeben lassen können:
/* portabel_readdir.c */ #include <stdio.h> #include <stdlib.h> #ifdef __unix__ #include <dirent.h> #include <sys/types.h> /* UNIX-Funktion zum Ausgeben des kompletten Verzeichnisses */ void list_dir(const char *path) { DIR *dirptr; struct dirent *dir; if ((dirptr=opendir(path)) == NULL) return; while((dir=readdir(dirptr)) != NULL) printf("%s\n",dir->d_name); closedir(dirptr); } #elif __WIN32__ || _MSC_VER #include <windows.h> /* Win32-Funktion zum Ausgeben des kompletten Verzeichnisses */ void list_dir(const char *path) { WIN32_FIND_DATA dir; HANDLE fhandle; char directory[256]; /* unsicher, besser wäre - falls vorhanden - snprintf() */ sprintf(directory,"%s\\*.*",path); /* Handle auf das Verzeichnis directory */ if ((fhandle=FindFirstFile(directory,&dir)) != INVALID_HANDLE_VALUE) { do { /* Verzeichnis auslesen */ printf("%s\n", dir.cFileName); } while(FindNextFile(fhandle,&dir)); } FindClose(fhandle); } #endif int main(int argc,char *argv[]) { if (argc < 2) list_dir("."); else list_dir(argv[1]); return EXIT_SUCCESS; }
Bei der Win32-Funktion wurden hier die MS-DOS-ähnlichen Funktionen findfirst() und findnext() verwendet. Die Funktion FindFirstFile() gibt ein Filehandle auf die erste Datei im Verzeichnis zurück, während FindNextFile() das Handle immer um eine Position weitersetzt, bis keine Dateien mehr im Verzeichnis zum Lesen vorhanden sind. FindClose() schließt das Filehandle wieder.
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.