Forum: PC-Programmierung Mit C mehrstufige Verzeichnisstruktur durchsuchen;


von interrupt (Gast)


Lesenswert?

Stehe gerade etwas auf dem Schlauch.
Ich muss mit einem C-Programmm eine mehrstufige Verzeichnisstruktur nach 
einem gewissen Dateityp (z.B. *.txt) durchsuchen.
Hat dazu ev. jemand einen Schnipsel Beispielcode oder einen Link ?
THX, interrupt

von Dirk B. (dirkb2)


Lesenswert?

Welches Betriebssystem?

Warum C?
Jede moderne Scriptsprache macht das einfacher.

von A. S. (Gast)


Lesenswert?

1
char *pattern = "*.txt";
2
DirRef dir  = getDirRef();
3
PathRef path;
4
int i;
5
    
6
   for(i=0; path=findFile(dir, pattern, i); i++)
7
   {
8
   }

mit findFile als Funktion, die einen kompletten Pfad auf das erste, 
zweite, ... File mit dem Pattern zurück liefert. Beachte das = (nicht 
==) im for.

Ansonsten vielleicht schreiben, welche Strukturen und Funktionen Du 
stattdessn so hasst.

von Yalu X. (yalu) (Moderator)


Lesenswert?

interrupt schrieb:
> Ich muss mit einem C-Programmm eine mehrstufige Verzeichnisstruktur nach
> einem gewissen Dateityp (z.B. *.txt) durchsuchen.
> Hat dazu ev. jemand einen Schnipsel Beispielcode oder einen Link ?

Ja:

  https://ftp.gnu.org/gnu/findutils/

von Schlaumaier (Gast)


Lesenswert?

Musst du Rekursiv machen.

Also die selbe Routine immer wieder selbst aufrufen. Mit einen anderen 
Parameter.

Du brauchst dazu 2 SUB-Routinen.

Die erste liest den Ordner, die 2 die Files darin (mit Filter)

BEIDE werden von einer Hauptroutine als SUB aufgerufen.

Diese VB-Routine sucht alle Order im Angegeben Pfad.

 Private Sub SucheAlleOrdner(ByVal Pfad As String)
    Dim AlleOrdner() As String
    'anz_pfad = 0
    x = 0
    AlleOrdner = Directory.GetDirectories(Pfad) ' da ist das File-Objekt 
gefragt
    For i As Integer = 0 To AlleOrdner.Length - 1
      If AlleOrdner(i) <> Pfad Then
        AlleOrdner(i) = LCase(AlleOrdner(i))
        hp_l_liste.Items.Add(AlleOrdner(i)) ' <- übergibt alle Ordner im 
Hauptorder an ein List-Element
        x = x + 1
        my_pfad(x) = AlleOrdner(i)
        Call SucheAlleOrdner(AlleOrdner(i)) ' <- Der rekursive Aufruf 
!!!
      End If
    Next i

  End Sub

Wenn du die hast, hast du ein Array mit allen Ordnern im Pfad.
Dann musst du in einer 2 Routine die Files im Ordner aufrufen (absuchen) 
und damit machen was du willst halt.

Wie du dem Code in C umschreibst weiß ich nicht, dürfte aber nicht so 
schwer sein. Kommt halt auf der FILE-Objekt an (ich benutze System.io in 
den Fall da.

von Schlaumaier (Gast)


Lesenswert?

Nachtrag:

Du kannst auch Trick 17 benutzen.

Shell aufrufen dann

dir *.txt /s /b >txt-liste.txt

Und dann die Liste mit Imput einlesen und verarbeiten.

Ist für die schnelle faule einmalige Methode.

Und das geht sogar einfach ohne Tools.

von interrupt (Gast)


Lesenswert?

A. S. schrieb:
> mit findFile als Funktion, die einen kompletten Pfad auf das erste,
> zweite, ... File mit dem Pattern zurück liefert. Beachte das = (nicht
> ==) im for.

Vielen Dank, damit sollte ich weiter kommen, werde ich dann gleich mal 
testen.

Betriebssystem ist Windows 10.
Der Code sollte aber am besten betriebssystemunabhängig sein.
Es soll ein bestehendes C-Programm um die Funktion erweitert werden.

von Ingo W. (uebrig) Benutzerseite


Angehängte Dateien:

Lesenswert?

Vielleicht kannst du dieses Beispiel für deine Zwecke anpassen.

procDir wird vom Hauptprogramm aufgerufen und ruft sich für 
Unterverzeichnisse selbst rekursiv auf.
procFile wird für reguläre Dateien aufgerufen, prüft die Endung, wenn 
nicht die Richtige (hier: pdf) beendet es sich, ansonsten wird hier die 
Datei umbenannt, indem eine Zeitstempel vor den alten Namen vorgesetzt 
wird.
Gesamt 75 Zeilen Code.

von Walter K. (walter_k488)


Lesenswert?

interrupt schrieb:
>
> Der Code sollte aber am besten betriebssystemunabhängig sein.
> Es soll ein bestehendes C-Programm um die Funktion erweitert werden.

Komplett OS unabhängig? Das ist dann aber schon eine ganz andere Nummer 
- denn das würde ja bedeuten, auf OS-basierte Librariers zu verzichten - 
und nicht wirklich realisierbar!

von Ingo W. (uebrig) Benutzerseite


Lesenswert?

Walter K. schrieb:
> Komplett OS unabhängig? Das ist dann aber schon eine ganz andere Nummer
> - denn das würde ja bedeuten, auf OS-basierte Librariers zu verzichten -
> und nicht wirklich realisierbar!

Hängt davon ab, die der Compiler die Bibliotheksfunktionen in OS-API 
Aufrufe umsetzt.
Mein Beispiel von oben, sieht zwar erstmal nach Unix aus, wenn es aber 
mit MinGw (gcc für Windows) übersetzt wird, funktioniert es sogar so wie 
es das steht, mit dem Pfadtrenner '/' in Zeile 55.

von Schlaumaier (Gast)


Lesenswert?

interrupt schrieb:
>
> Der Code sollte aber am besten betriebssystemunabhängig sein.
> Es soll ein bestehendes C-Programm um die Funktion erweitert werden.

Halte ich für fast unmöglich.

Allein das Thema RECHTE-Verwaltung zum Zugriff auf Ordner ist ne 
Geschichte für sich. Selbst wenn man "NUR" Windows + Linux nimmt rennt 
man schon gewaltig gegen eine Wand.

Davon abgesehen, basieren solche Routinen auf Zugriffe das (jeweiligen) 
File-System. Ich habe in meinen VB-Code das File-System angeben weil ich 
selbst da, mehre Systeme zu Verfügung habe.   Also schlicht und 
ergreifend VERGISS ES .

von Dirk B. (dirkb2)


Lesenswert?

Ingo W. schrieb:
> funktioniert es sogar so wie
> es das steht, mit dem Pfadtrenner '/' i

Das kann Windows schon lange.

von Cartman (Gast)


Lesenswert?

> Das kann Windows schon lange.

Es kann immer noch keinen Kaffee kochen oder einen Ball holen.

von Schlaumaier (Gast)


Lesenswert?

Cartman schrieb:
> Es kann immer noch keinen Kaffee kochen oder einen Ball holen.

NOCH Nicht.  Das geht erst ab IoT 2.0 wenn die Smart-Watch die den 
Bio-Rhythmus , Plus / Herzschlag  + EKG kontrolliert erlaubt, das die 
Kaffeemaschine gestartet werden darf, welches Windows dann automatisch 
macht.

Ist übrigens inzwischen mit TOP-Kaffee-Vollautomaten der neusten 
Generation sogar per App möglich.

Selbstverständlich geht dann automatisch ein Hinweis in die digitale 
Krankenakte das du dein Körper mit einer legalen Droge geschädigt hast.
Worauf die Krankenkasse dein Beitrag um 10 % anhebt.

Was den Ball holen angeht. Windows schickt via Wlan eine Nachricht an 
dein Pflegeroboter, den du hast, weil dein Körper durch den vielen 
Kaffee so geschädigt ist, das der Pfegeroboter dann dein Ball holt. ;)

von 123 (Gast)


Lesenswert?

Eine Erweiterung hin zu c++ mit std::filesystem ist keine Alternative?

von Rolf M. (rmagnus)


Lesenswert?

Schlaumaier schrieb:
>> Der Code sollte aber am besten betriebssystemunabhängig sein.
>> Es soll ein bestehendes C-Programm um die Funktion erweitert werden.
>
> Halte ich für fast unmöglich.

In Standard-C++ gibt's das. Wobei es da auch gewisse Fallstricke gibt. 
Lustigerweise dann aber eher sowas unerwartetes wie der verwendete 
String-Typ. std::filesystem::path nutzt als String-Typ unter Linux 
std::string, unter Windows stattdessen std::wstring.

> Allein das Thema RECHTE-Verwaltung zum Zugriff auf Ordner ist ne
> Geschichte für sich.

Da muss man nix "verwalten". Einfach öffnen und schauen, ob's klappt 
oder nicht.

von interrupt (Gast)


Lesenswert?

Ingo W. schrieb:
> procDir wird vom Hauptprogramm aufgerufen und ruft sich für
> Unterverzeichnisse selbst rekursiv auf.
> procFile wird für reguläre Dateien aufgerufen, prüft die Endung, wenn
> nicht die Richtige (hier: pdf) beendet es sich, ansonsten wird hier die
> Datei umbenannt, indem eine Zeitstempel vor den alten Namen vorgesetzt
> wird.
> Gesamt 75 Zeilen Code.

Super, vielen Dank !
Das Programm ist compiliert und funktioniert.
Ich verwende den MINGW64-Compiler mit Eclipse.

"st_mtim" musste ich in "st_mtime" ändern

und Zeile 63 musste
von "procFile(root, d->d_name, sb.st_mtime.tv_sec);"
in  "procFile(root, d->d_name, sb.st_mtime);"

geändert werden.

von Le X. (lex_91)


Lesenswert?

Schlaumaier schrieb:
> VERGISS ES .

Ehrlich?
Du kapitulierst schon wenn es darum geht einen Verzeichnisbaum nach 
Text-Dateien zu durchsuchen?

von pointer (Gast)


Angehängte Dateien:

Lesenswert?

Ingo W. schrieb:
> Vielleicht kannst du dieses Beispiel für deine Zwecke anpassen.
>
> procDir wird vom Hauptprogramm aufgerufen und ruft sich für
> Unterverzeichnisse selbst rekursiv auf.
> procFile wird für reguläre Dateien aufgerufen, prüft die Endung, wenn
> nicht die Richtige (hier: pdf) beendet es sich, ansonsten wird hier die
> Datei umbenannt, indem eine Zeitstempel vor den alten Namen vorgesetzt
> wird.
> Gesamt 75 Zeilen Code.

Ich habe Deinen Code jetzt so angepasst, dass er eine 
Verzeichnisstruktur durchsucht und darin enthaltene Textdateien (*.txt) 
anzeigt.
Datzu habe ich testweise einen vierstufigen Verzeichnisbaum angelegt und 
darin kleine Textdateien gespeichert.
Der Aufbau ist im Diagramm in der Grafik ersichtlich, "dirx" steht dabei 
für Verzeichnisse, "textx" für Textdateien.
Das Programm funktioniert grundsätzlich, es findet und zeigt alle 
vorhandenen Verzeichnisse und Dateien an.
Allerdings ist das Verhalten doch recht verwunderlich, weil die Anzeige 
der Verzeichnisse und der darin enthaltenen Dateien nicht synchronisiert 
ist.

Bei den Verzeichnissen (blaue Pfeile) ist der Ablauf logisch 
nachvollziehbar, er beginnt ganz oben am Einstiegsverzeichnis "data" und 
arbeitet sich dann bis ganz nach unten durch, dann springt er auf 
benachbarte Verzeichnisäste.

Bei den Dateien (rote Pfeile) beginnt die Anzeige aber erst, wenn das 
Programm schon ganz unten in der Verzeichnishierachie angekommen ist 
(Punkt "dir4.1") und danach arbeitet sich die Dateianzeige recht kreativ 
weiter durch, bis auch alle Dateien angezeigt wurden, allerdings in 
einer anderen Reihenfolge als die Verzeichnisse.

Das Problem ist also, dass man im Programmablauf die gefundenen Dateien 
so nicht direkt den Verzechnissen zurordnen kann, in denen sie enthalten 
sind.

Hat jemand eine Erklärung für die Logik dieses Programmablaufen ?

von Yalu X. (yalu) (Moderator)


Lesenswert?

pointer schrieb:
> Hat jemand eine Erklärung für die Logik dieses Programmablaufen ?

Dein Programm bearbeitet offensichtlich in jedem Verzeichnisknoten erst 
die Unterverzeichnisse und dann die regulären Dateien. Wenn du das als 
unlogisch empfindest, kannst du diese Reihenfolge ja umdrehen (also erst 
die regulären Dateien anzeigen und dann erst in die Unterverzeichnisse 
absteigen).

von pointer (Gast)


Lesenswert?

Yalu X. schrieb:
> Dein Programm bearbeitet offensichtlich in jedem Verzeichnisknoten erst
> die Unterverzeichnisse und dann die regulären Dateien. Wenn du das als
> unlogisch empfindest, kannst du diese Reihenfolge ja umdrehen (also erst
> die regulären Dateien anzeigen und dann erst in die Unterverzeichnisse
> absteigen).

Na ja, bei den ersten drei Verzeichnisknoten (dir1, dir2.1, dir3.1) 
bearbeitet das Programm die darin enthaltenen Dateien ja gar nicht 
(wobei sie ja erst mal gar nicht bearbeitet, sondern nur angezeigt 
werden sollen), und erst beim vierten Knoten (dir4.1) wird die darin 
enthaltene Textdatei angezeigt.
Die Anzeige der in den Knoten darüber enthaltenen Dateien wird dann 
nachgeholt, wobei das nicht synchron mit der Durchsuchung der 
Verzeichnisse abläuft.
Mir erschliesst sich die Logik nicht noch so ganz.

von Ingo W. (uebrig) Benutzerseite


Lesenswert?

In dem Beispielprogramm gibt es keinerlei Sortierung, die Objekte 
(Dateien, Verzeichnisse) werden in der Reihenfolge abgearbeitet, in der 
die "readdir"-Funktion sie liefert. Diese Funktion muss die Daten aus 
einem entsprechenden Betriebssystem-API beziehen. Unter Linux ist das 
die rohe Reihenfolge in der die Einträge im Verzeichnis stehen (sie im 
jungfräulichen Verzeichnis angelegt wurden). Unter Windows habe ich es 
so in Erinnerung, dass sie hier in alphabetischer Reihenfolge geliefert 
werden.
Wenn du eine bestimmte Reihenfolge haben möchtest, müsstest du die Daten 
des Verzeichnisses erst sortieren und dann verarbeiten. Da man vorher 
schlecht weiß, wie viele Verzeichniseinträge kommen, mache ich so etwas 
gern mit verketteten Listen.

von Markus L. (rollerblade)


Lesenswert?

> strcpy(st,cp);
Mach da mal eine Längenbeschränkung rein, eignet sich sonst für Stack 
Hacks.

von Ingo W. (uebrig) Benutzerseite


Angehängte Dateien:

Lesenswert?

Weil ich das auch selbst gut gebrauchen kann, habe ich das Gerippe jetzt 
mal so umgearbeitet, dass das gelesene Verzeichnis vor der Abarbeitung 
erst sortiert wird (Verzeichnisse alphabetisch aufsteigend, dann Dateien 
alphabetisch aufsteigend).

Da es mir um das Umbenennen von Dateien geht, wird der 
"procFile"-Funktion, in einer Struktur, das Verzeichnis und der 
Dateiname getrennt übergeben, damit für den neuen Dateinamen nicht das 
Verzeichnis wieder extrahiert werden muss.

Markus L. schrieb:
> Mach da mal eine Längenbeschränkung rein, eignet sich sonst für Stack
> Hacks.

Die Puffer werden mit einer Länge von PATH_MAX angelegt. Hier gehe ich 
mal blauäugig davon aus, dass das lokale Dateisystem nicht mehr hergibt. 
Da dies hier eigentlich gelesen wird, sollte der Einfluss von außen 
begrenzt sein.

von interrupt (Gast)


Lesenswert?

Ingo W. schrieb:
> Weil ich das auch selbst gut gebrauchen kann, habe ich das Gerippe jetzt
> mal so umgearbeitet, dass das gelesene Verzeichnis vor der Abarbeitung
> erst sortiert wird

Mein Compiler (mingw64) in Exlipse meldet eine Warnung und einen Fehler 
:


23:32:59 **** Rebuild of configuration Debug for project 
mp_test_dirs_files ****
Info: Internal Builder is used for build
g++ -O0 -g3 -Wall -c -fmessage-length=0 -o main.o "..\\main.cpp"
..\main.cpp: In function 'void procDir(char*)':
..\main.cpp:40:9: warning: suggest parentheses around assignment used as 
truth value [-Wparentheses]
  while(d=readdir(di)){
        ~^~~~~~~~~~~~
..\main.cpp:45:15: error: invalid conversion from 'void*' to 'set*' 
[-fpermissive]
    next=calloc(1,sizeof(set));
         ~~~~~~^~~~~~~~~~~~~~~

23:33:00 Build Failed. 1 errors, 1 warnings. (took 1s.103ms)

von interrupt (Gast)


Lesenswert?

Mit
      "next = (set*) calloc(1, sizeof(set));"
statt "next=calloc(1,sizeof(set));"

in Zeile 45 klappt das Compilieren.

von 🐧 DPA 🐧 (Gast)


Lesenswert?

interrupt schrieb:
> Mit
>       "next = (set*) calloc(1, sizeof(set));"
> statt "next=calloc(1,sizeof(set));"
>
> in Zeile 45 klappt das Compilieren.

Das deutet darauf hin, dass du den Code als C++ statt als C kompiliert 
hast. In C++ geht es nicht ohne den Cast. In C braucht man den nicht, 
und sollte weggelassen werden. Unnötiges Casten ist in C Code nicht 
gerne gesehen.

von Rolf M. (rmagnus)


Lesenswert?

🐧 DPA 🐧 schrieb:
> Das deutet darauf hin, dass du den Code als C++ statt als C kompiliert
> hast.

Darauf deutet auch das hin:

interrupt schrieb:
> g++ -O0 -g3 -Wall -c -fmessage-length=0 -o main.o "..\\main.cpp"


> In C braucht man den nicht,
> und sollte weggelassen werden. Unnötiges Casten ist in C Code nicht
> gerne gesehen.

Das Problem dabei ist, dass es an so einer Stelle einen Fehler verdecken 
kann.

von interrupt (Gast)


Angehängte Dateien:

Lesenswert?

Ingo W. schrieb:
> Weil ich das auch selbst gut gebrauchen kann, habe ich das Gerippe jetzt
> mal so umgearbeitet, dass das gelesene Verzeichnis vor der Abarbeitung
> erst sortiert wird (Verzeichnisse alphabetisch aufsteigend, dann Dateien
> alphabetisch aufsteigend).

Das Program funktioniert, aber die im Beitrag vom 12.02.2022 00:16 
erwähnte und per Grafik visualisierte Asynchronität zwischen der Anzeige 
der Verzeichnisse und der darin enthaltenen Dateien besteht weiter.

Ich habe das Urspungsprogramm vom Beitrag am 05.02.2022 13:37 jetzt mal 
in zwei Varianten so überarbeitet, dass die in den Verzeichnissen 
enthaltenen Dateien dann angezeigt werden, wenn sich das Programm auch 
in diesem Verzeichnissen befindet.

Das erste Programm (main_A.cpp) verwendet dazu readdir(), 
FindFirstFile() und FindNextFile().
Das zweite Programm (main_B.cpp) nutzt readdir(),_findfirst() und 
_findnext().

Durchsucht wird der Verzeichnisbaum in .\data (gepackt in "data.7z")

Der Aufruf des Programme erfolgt mit <programm.exe> ".\data"

Der Code wurde mit mingw64 in Eclipse compiliert

von Einer (Gast)


Lesenswert?

interrupt schrieb:
> Das Program funktioniert

Meine Güte... Wieviele Stunden hast Du gebraucht um die ganzen 
Code-Fetzen zusammen zu googeln?

Das Programm funktioniert übrigens nicht. Es ist Schrott. Du hast einen 
kapitalen Denkfehler.

Du brauchst z.B. nur opendir(), readdir(), stat() und closedir().

Stell Dir ein Verzeichnis wie eine Textdatei vor mit Zeilen. Jede Zeile 
ist ein Eintrag im Verzeichnis. Der Einträge bestehen aus dem 
"Datei"namen.

Mit opendir() öffnest Du ein Verzeichnis. "Öffnen" bedeutet nicht ins 
Verzeichnis wechseln oder sonstwie irgendetwas. Sondern nur öffnen, so 
wie Du eine Datei zu lesen öffnen würdest. Du öffnest ein Telefonbuch 
(Kennt das hier noch jemand, also aus Papier?).

Mit readdir() liest Du den nächsten Eintrag aus einem geöffneten 
Verzeichnis. Der Eintrag besteht u.a. aus dem Namen. Und nur dem Namen. 
Ohne Pfadangabe.

Danach baust Du aus dem Verzeichnisnamen und Eintragsnamen den Pfad zu 
einer "Datei".

Mit stat() schaust Du nach, was die "Datei" ist. Ist es eine echte 
Datei, oder ist es nur ein weiteres Verzeichnis?

Mit closedir() schließt Du das Verzeichnis wieder. Das Betriebssystem 
dankt Dir dafür.

Also, sieht der Code ungefähr so aus (Pseudocode):

1
void processDir(dirName)
2
{
3
  print("Untersuche Verzeichnis:" + dirName);
4
5
  dir = opendir(dirName);
6
7
  while ((entry = readdir(dir))
8
  {
9
    if (entry.name != "." && entry.name != "..")
10
    {
11
      path = dirName + "\" + entry.name;
12
13
      info = stat(path);
14
15
      if (info.type == DIRECTORY)
16
      { 
17
        print("Verzeichnis gefunden:" + path);
18
        processDir(path);
19
      }
20
      else
21
      {
22
        print("Datei gefunden:" + path);
23
      }
24
     }
25
  }
26
27
  closedir(dir);
28
}

von interrupt (Gast)


Lesenswert?

Einer schrieb:
> trivialer Quuatsch

ja und, glaubst du wirklich, du erzählst damit etwas neues ?

Fakt ist allerdings, dass wie bereits geschrieben, die Verzeichnisse und 
die darin enthaltenen Dateien mit readdir() nicht synchron ausgegeben 
werden.
Begriffsstutzige wie Du können sich dazu auch das Diagramm vom 
12.02.2022 00:16 anshen.
Darin sieht man z.B., dass die Datei im obersten Verzeichnis erst 
augegeben wird, wenn mit readdir() die ganze Verzeichnisstruktur 
rekursiv durchlaufen ist.

Ich bevorzuge allerings die Ausgabe buzw. Berabreitung der in einem 
Verzeichnis enthaltenen Dateien genau dann, wenn sich das Programm auch 
in diesem Verz3eichnis befindet.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.