1 Theorie | |
→ | 1.2 Häufig auftretende Problemstellungen |
Verzeichnis durchsuchen unter Windows | |
Verzeichnis durchsuchen unter Linux/Unix | |
Einfaches Beispiel | |
Standard-gerechtes Beispiel | |
Multi-Thread-Programm | |
Aufgabe |
Zum Durchsuchen von Verzeichnissen dient unter Windows die
Funktionsfamilie _findfirst64()/_findnext64()/_findclose().
Diese Funktionen suchen nach Dateien, deren Name einem vorgegebenen
Muster entspricht.
Um alle Einträge in einem Verzeichnis zu finden, wird als Muster
der Verzeichnisname verwendet, daran wird ein Backslash angehangen
und der Platzhalter * für "alle Dateien".
Der Backslash hat in String-Literalen eine Sonderfunktion, er
ermöglicht z.B. die Angabe eines Newlines über die
Backslash-Sequenz "\n". Um einen String mit Backslashes zu
erzeugen, muss im String-Literal jeweils eine Folge von zwei
Backslashes geschrieben werden.
Die Funktion _findfirst64() öffnet ein Verzeichnis zum
Durchsuchen und lädt bei Erfolg den ersten gefundenen Eintrag in
den Ergebnis-Puffer. Die Funktion gibt einen Handle als Ergebnis
zurück. Dabei handelt es sich um einen ganzzahligen Wert, für den
das Betriebssystem weitere Ressourcen verwaltet. Der Wert -1 dient
als Kennung für einen aufgetretenen Fehler. Bei Erfolg können dann
weitere Einträge mit _findnext64() gesucht werden. Diese
Funktion gibt bei Erfolg 0 zurück. Nach Abschluss der Suche muss
der von _findfirst64() gelieferte Handle wieder geschlossen
werden, damit die mit dem Handle verknüpften Ressourcen wieder
freigegeben werden.
Der Ergebnispuffer vom Typ struct __finddata64_t beinhaltet
verschiedene Angaben für den jeweiligen Eintrag, u.a.:
sowie Erstellung-, Änderungs- und Zugriffszeitpunkt und weitere Attribute.
Mit _findfirst64()/_findnext64() und struct
__finddata64_t werden jeweils 64-Bit-Zahlen für Dateigröße und die
Zeitstempel verwendet.
Es existieren weitere Varianten, die kleinere Zahlen verwenden,
diese werden heute nicht mehr empfohlen.
Zur Internationalisierung gibt es weitere Varianten _wfindfirst64()/_wfindnext64() mit struct _wfinddata64_t, diese arbeiten mit WCHAR-Strings.
/** @file ex067.c Beispiel fuer Durchsuchen eines Verzeichnisses.
Das Programm gibt die im Verzeichnis C:\Temp enthaltenen Eintraege
aus.
*/
/* Copyright (C) 2014-2017 - HS Schmalkalden. All rights reserved. */
#include <io.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <process.h>
/** Suchmuster: Verzeichnisname mit angehangenem Backslash und
Asterisk (Stern).
Im Beispiel wird das Verzeichnis C:\Temp durchsucht.
*/
static const char suchmuster[] = { "C:\\Temp\\*" };
/** Hauptprogramm.
@param argc Anzahl der Kommandozeilenargumente.
@param argv Feld mit Kommandozeilenargumenten.
Die Kommandozeilenargumente werden hier nicht
verarbeitet.
@return 0 bei Erfolg, alle anderen Werte zeigen Fehler an.
*/
int
main(int argc, char *argv[])
{
struct __finddata64_t fileinfo;
intptr_t searchid;
int result;
int exval = EXIT_FAILURE;
/* Verzeichnis oeffnen, Test auf Erfolg */
errno = 0;
searchid = _findfirst64( suchmuster, &fileinfo );
if (-1 != searchid) {
/* Verzeichnis erfolgreich geoeffnet, Erfolg vermerken */
exval = EXIT_SUCCESS;
/* Alle Eintraege des Verzeichnisses durchlaufen */
do {
/* Letzten gefundenen Eintrag verarbeiten */
fputs("Gefundener Eintrag: ", stdout);
fputs(fileinfo.name, stdout);
fputc('\n', stdout);
/* Naechsten Eintrag suchen */
errno = 0;
result = _findnext64(searchid, &fileinfo);
if (0 != result) {
switch (errno) {
case EINVAL: {
fputs(
"FEHLER: Betriebssystem meldete Problem!\n", stderr
);
exval = EXIT_FAILURE;
} break;
case ENOENT: {
/* Durchsuchen beendet */
} break;
case ENOMEM: {
fputs(
"FEHLER: Zu wenig Arbeitsspeicher oder Name zu lang!\n",
stderr
);
exval = EXIT_FAILURE;
} break;
default: {
fputs(
"FEHLER: Ein unbekannter Fehler trat auf!\n",
stderr
);
exval = EXIT_FAILURE;
} break;
}
}
} while(0 == result);
/* Verzeichnis schliessen, Durchsuchen beenden, Ressourcen freigeben */
_findclose(searchid);
} else {
/* Oeffnen des Verzeichnisses fehlgeschlagen, Fehlermeldung ausgeben */
switch (errno) {
case EINVAL: {
fputs("FEHLER: Ungueltiger Dateiname...!\n", stderr);
} break;
case ENOENT: {
fputs("FEHLER: Keine passende Datei gefunden!\n", stderr);
} break;
case ENOMEM: {
fputs("FEHLER: Zu wenig Arbeitsspeicher!\n", stderr);
} break;
default: {
fputs("FEHLER: Ein unbekannter Fehler trat auf!\n", stderr);
} break;
}
}
exit(exval); return exval;
}
/* vim: set ai sw=4 ts=4 expandtab : */
Unter Linux/Unix werden die Funktionen opendir()/readdir()/closedir() verwendet. Die Funktion opendir() öffnet ein Verzeichnis, legt eine Struktur vom Typ DIR an und gibt einen Zeiger auf die neu angelegte Struktur zurück. Bei Fehlern wird NULL zurückgegeben und die Variable errno gesetzt. Der Inhalt der Strukur ist hier nicht von Interesse. Der von opendir() zurückgegebene Zeiger wird mit readdir() verwendet, um das Verzeichnis zu durchsuchen. Nach dem Durchsuchen wird mit closedir() das Verzeichnis geschlossen und der Speicherplatz der DIR-Struktur wieder freigegeben.
Die Funktion readdir() liest den nächsten
Verzeichniseintrag in einen internen statischen Puffer ein und gibt
einen Zeiger auf diesen statischen Puffer zurück.
Ist das Ende des Verzeichnisses erreicht oder tritt ein Fehler auf,
wird NULL zurückgegeben.
/** @file ex068.c Beispiel fuer Directory-Durchsuchen unter Linux/UNIX.
Das Programm gibt die Namen der Eintraege im aktuellen Verzeichnis aus.
*/
/* Copyright (C) 2014-2017 - HS Schmalkalden. All rights reserved. */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
int
main(int argc, char *argv[])
{
DIR *pdir; /* Directory */
struct dirent *pent; /* Directory-Eintag */
int exval = EXIT_FAILURE; /* Exit-Status-Code */
/* Verzeichnis oeffnen, auf Erfolg testen */
errno = 0;
pdir = opendir(".");
if (NULL != pdir) { /* Verzeichnis erfolgreich geoeffnet */
/* Verzeichnis durchsuchen */
while ( NULL != (pent = readdir(pdir)) ) {
fputs("Gefundener Eintrag: ", stdout);
fputs(pent->d_name, stdout);
fputc('\n', stdout);
}
/* Verzeichnis wieder schliessen */
closedir(pdir);
}
else { /* Fehler beim Verzeichnis-Oeffnen */
switch (errno) {
case EACCES: {
fputs("FEHLER: Keine Berechtigung!\n", stderr);
} break;
case EMFILE: {
fputs(
"FEHLER: Prozess hat zu viele Dateien geoeffnet!\n", stderr
);
} break;
case ENFILE: {
fputs(
"FEHLER: Zu viele Dateien sind im System geoeffnet!\n",
stderr
);
} break;
case ENOENT: {
fputs("FEHLER: Verzeichnis existiert nicht!\n", stderr);
} break;
case ENOMEM: {
fputs("FEHLER: Zu wenig Speicher verfuegbar!\n", stderr);
} break;
case ENOTDIR: {
fputs("FEHLER: Der Dateiname ist kein Verzeichnis!\n", stderr);
} break;
default: {
fputs("FEHLER: Unbekanntes Problem aufgetreten!\n", stderr);
} break;
}
}
exit(exval); return exval;
}
/* vim: set ai sw=4 ts=4 expandtab : */
Im Beispiel ex068.c wird in Zeile 26 gegen Regel EXP45-C des Cert C Coding Standards (Keine Kombination von Vergleichen und Zuweisungen in Bedingungen) verstoßen, da die Kombination aus Zuweisung und Vergleich eine einfache Programmstruktur ergibt. Vor dem Vergleich wird der Klammer-Ausdruck berechnet, in diesem wird der Variable pent das Ergebnis der readdir()-Funktion zugewiesen.
In Beispiel ex069.c ist die Programmstruktur geringfügig komplexer, dafür genügt das Programm dem Standard.
/** @file ex068.c Beispiel fuer Directory-Durchsuchen unter Linux/UNIX.
Das Programm gibt die Namen der Eintraege im aktuellen Verzeichnis aus.
*/
/* Copyright (C) 2014-2017 - HS Schmalkalden. All rights reserved. */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
int
main(int argc, char *argv[])
{
DIR *pdir; /* Directory */
struct dirent *pent; /* Directory-Eintag */
int exval = EXIT_FAILURE; /* Exit-Status-Code */
/* Verzeichnis oeffnen, auf Erfolg testen */
errno = 0;
pdir = opendir(".");
if (NULL != pdir) { /* Verzeichnis erfolgreich geoeffnet */
/* Verzeichnis durchsuchen */
do {
pent = readdir(pdir);
if (NULL != pent) {
fputs("Gefundener Eintrag: ", stdout);
fputs(pent->d_name, stdout);
fputc('\n', stdout);
}
} while(NULL != pent);
/* Verzeichnis wieder schliessen */
closedir(pdir);
}
else { /* Fehler beim Verzeichnis-Oeffnen */
switch (errno) {
case EACCES: {
fputs("FEHLER: Keine Berechtigung!\n", stderr);
} break;
case EMFILE: {
fputs(
"FEHLER: Prozess hat zu viele Dateien geoeffnet!\n", stderr
);
} break;
case ENFILE: {
fputs(
"FEHLER: Zu viele Dateien sind im System geoeffnet!\n",
stderr
);
} break;
case ENOENT: {
fputs("FEHLER: Verzeichnis existiert nicht!\n", stderr);
} break;
case ENOMEM: {
fputs("FEHLER: Zu wenig Speicher verfuegbar!\n", stderr);
} break;
case ENOTDIR: {
fputs("FEHLER: Der Dateiname ist kein Verzeichnis!\n", stderr);
} break;
default: {
fputs("FEHLER: Unbekanntes Problem aufgetreten!\n", stderr);
} break;
}
}
exit(exval); return exval;
}
/* vim: set ai sw=4 ts=4 expandtab : */
Die Funktion readdir() schreibt Daten für den gefundenen
Verzeichnis-Eintrag in einen statischen Puffer. In Programmen mit
mehreren Threads kann diese Funktion nicht benutzt werden, es muss
stattdessen readdir_r() verwendet werden.
Im Rahmen dieses Einführungskurses wird Thread-Programmierung nicht
behandelt.