Verzeichnise Rekursiv ausgeben...



  • Habe in der FAQ folgenden Code gefunden:

    #include <windows.h>
    #include <iostream>
    
    using namespace std;
    
    #pragma comment(lib,"user32.lib")
    
    int main()
    {
    	HANDLE fHandle;
    	WIN32_FIND_DATA wfd;
    
    	fHandle=FindFirstFile("C:\\*",&wfd);
    
    	do
    	{
    		// Eintrag nur behandeln, wenn es nicht . oder .. ist (werden nur bei Unterverzeichnissen mit zurückgeliefert)
    		// hier könnte man z.B. auch mit lstrcmp auf . und .. vergleichen, was allerdings nicht ganz so effizient ist
    		if (!( (wfd.cFileName[0]=='.') && ( (wfd.cFileName[1]=='.' && wfd.cFileName[2]==0) || wfd.cFileName[1]==0 ) ))
    		{
    			if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
    			{				
    				cout << "Verzeichnis: " << wfd.cFileName << endl;
    
    				// Datei ist keine, sondern ein Verzeichnis...
    				// Hier könnte man dasselbe nochmal machen, um auch die
    				// Unterverzeichnisse zu scannen ;-)
    			}
    			else
    			{				
    				cout << "Datei: " << wfd.cFileName << endl;
    			}
    		}
    	}
    	while (FindNextFile(fHandle,&wfd));
    	FindClose(fHandle);
    
    	system("pause");
    }
    

    Jetzt steht in der Schleife wenn sich das Attribut um ein Verzeichnis handelt kann man hier die weiteren Unterverzeichnise ausgeben, wie funktioniert das?



  • Hi!

    Bevor ich antworte muss ich leider ein wenig schimpfen ;):
    Deine if-Abfrage auf die Punkte ist doof gestaltet, auch wenn sie aus der FAQ ist. Die UND- und ODER-Verknüpfungen solltest du kapseln, sonst könnte es zu Ungereimtheiten kommen, auch in der Lesbarkeit ⚠

    Weitere Unterverzeichnisse ausgeben:
    Dazu musst du an "C:\" den Verzeichnisnamen, wieder ein "\*" anhängen und nochmal mit FindFile-Krams suchen. Wenn du das dann noch für weitere Unterverzeichnisse ausgeben willst, würde sich eine rekursive Funktion anbieten. Also eine Funktion, die einen Verzeichnisnamen entgegen nimmt und die dazugehörigen Unterverzeichnisse/-Ordner ausgibt. Wenn es ein Unterverzeichnis gibt, dann wieder diese Funktion aufrufen mit dem neuen Unterverzeichnisnamen usw.

    Hand zum Gruß,
    Badestrand

    Edit & PS: Das stimmt schon, dass lstrcmp in dem Fall ineffizienter wäre, allerdings würde ich trotzdem damit auf "." und ".." vergleichen. Da beim Vergleichen ja nur 1 bzw 2 Bytes verglichen werden, ist der Performance-Verlust wirklich aufgrund der Lesbarkeit in Kauf zu nehmen.
    Ich verstehe dich vollkommen mit dem Argument der Performance, aber man sollte schon abwägen, Performance, Lesbarkeit und Komplexibilität. Z.B. ist das gesamte OOP sicherlich ein Quentchen langsamer als wenn man funktional programmiert, allerdings ist es wesentlich lesbarer und unkomplexer.
    Ein Aufruf nach FindFirstFile/FindNextFile kostet sicherlich 1000000 mal mehr Zeit als ein entsprechender Aufruf zu lstrcmp, in dieser Relation macht es also recht wenig Sinn auf die Performance zu achten 😉



  • Dazu habsch mir mal ne Funktion geschrieben, *kram*, *kram*, ...

    Ahh, gefunden 😃 ..., Voila:
    (Ist zwar etwas außen Kontext gerissen aber doch so unabhängig verwendbar 😉 )

    /*	Function Definition of:
    *
    *	ScanFoldersForFiles
    *
    *		The Function scans the specified folder (pszFolderParent)
    *		and ALL Subfolders for ALL Files. Any time a File is found
    *		the 'Handle-Result-Function' is called, pointed to by pFileProc.
    *
    */
    bool ScanFoldersForFiles(CPCSTRG pszFolderParent, bool(__cdecl* pFileProc)(CPCSTRG pFilePath, void* pData),
    						   void* pvUserData)
    {
    	if(pszFolderParent == NULL || pFileProc == NULL)
    		return (false);
    
    	WIN32_FIND_DATA	win32FinData;
    	HANDLE			hFile;
    	cString			strSearchArg,
    					strSrc(pszFolderParent);
    	if(strSrc[strSrc.length()] == SIGN('\\'))
    		strSrc.erase(strSrc.rfind(SIGN('\\'), cString::npos), strSrc.length());
    	if(strSrc.rfind(STRING("\\*"), cString::npos) == cString::npos)
    		strSrc.append(cString(STRING("\\*")), 0, 3);
    
    	hFile = FindFirstFile(strSrc.c_str(), &win32FinData);
    	if(hFile == INVALID_HANDLE_VALUE)
    		return (false);
    	strSrc.erase(strSrc.rfind(SIGN('*'), cString::npos));
    	do
    	{
    		if(!((win32FinData.cFileName[0] == SIGN('.')) &&
    		   ((win32FinData.cFileName[1] == SIGN('.') &&
    		   win32FinData.cFileName[2] == 0) ||
    		   win32FinData.cFileName[1] == 0)))
    		{
    			if(win32FinData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
    			{
    				strSearchArg = strSrc + cString(win32FinData.cFileName);
    				strSearchArg.append(cString(STRING("\\*")), 0, 3);
    				ScanFoldersForFiles(strSearchArg.c_str(), pFileProc, pvUserData);
    			}
    			else
    			{
    				if(!pFileProc((strSrc + cString(win32FinData.cFileName)).c_str(), pvUserData))
    					break;
    			}
    		}
    	}
    	while(FindNextFile(hFile, &win32FinData));
    
    	FindClose(hFile);
    	return (true);
    }
    

    Abhängigkeiten (Header):

    #include <Windows.h>
    #include <String>
    

    PS: Wie man sieht das Ding ist auch Zeichensatzunabhängig 😉 .



  • CodeFinder schrieb:

    PS: Wie man sieht das Ding ist auch Zeichensatzunabhängig 😉 .

    ➡ 👍 🤡

    Hoffe er ist nicht überfordert mit dem Code :xmas2:

    ... und lass mich raten, cString ist selbst geschrieben? 😃



  • Wenn ich auch raten darf:
    cString ist
    typedef std::string<TCHAR> cString;
    Right?



  • Weder noch 😃 ... :

    #ifdef UNICODE
      typedef std::wstring cString;
      // ...
    #else
      typedef std::string cString;
      // ...
    #endif // UNICODE
    

    Weiterhin ist ein CSTRG char bzw. wchar_t, ein PCSTRG eben ein char* bzw. wchar_t* und ein CPCSTRG ein const char* bzw. const wchar_t* 😉 .

    PS: @hustbaer:
    std::string ist schon ein instantiiertes Template, nochmal geht nicht^^.



  • Hab das mit den Datentypen so gelöst:

    typedef char CSTRG;
    typedef char* PCSTRG;
    typedef const char* CPCSTRG;
    

    Jetzt bekomme ich Compiler Fehler wegen dem SIGN und STRING:

    error C3861: "SIGN": Bezeichner wurde nicht gefunden.
    error C3861: "STRING": Bezeichner wurde nicht gefunden.
    

    [Edit] Welche 3 Parameter werden der Funktion ScanFoldersForFiles() übergeben?



  • kernel64 schrieb:

    Hab das mit den Datentypen so gelöst:

    typedef char CSTRG;
    typedef char* PCSTRG;
    typedef const char* CPCSTRG;
    

    Das würd ich besser so lösen:

    #ifndef UNICODE
      typedef char        CSTRG;
      typedef char*       PCSTRG;
      typedef const char* CPCSTRG;
    #else
      typedef wchar_t        CSTRG;
      typedef wchar_t*       PCSTRG;
      typedef const wchar_t* CPCSTRG;
    #endif // !UNICODE
    

    Denn sonst hast Du ja meine schöne Zeichensatzunabhängigkeit ruiniert 😉 .

    kernel64 schrieb:

    Jetzt bekomme ich Compiler Fehler wegen dem SIGN und STRING:

    error C3861: "SIGN": Bezeichner wurde nicht gefunden.
    error C3861: "STRING": Bezeichner wurde nicht gefunden.
    

    Jo der Einfachheit kannste das alles durch das Makro TEXT() ersetzen.

    kernel64 schrieb:

    [Edit] Welche 3 Parameter werden der Funktion ScanFoldersForFiles() übergeben?

    Beispielaufruf:

    bool __cdecl HandleFoundFiles(CPCSTRG pFilePath, void* pData)
    {
       // In pFilePath steht der Pfad zur gerade gefundenen Datei...
       static cString strInfoMsg(STRING("\n\n  Möchten Sie fortfahren ?"));
       if(MessageBox(NULL, (cString(pFilePath) + strInfoMsg).c_str(), STRING("Aktuelle Datei:"), MB_YESNO | MB_ICONINFORMATION) == IDYES)
           return (true);
       else
           return (false);
    
    /*
    Hinweis: Der Return-Wert bestimmt das weitere Vorgehen... . Wenn Du hier
    false zurückgibst, beendest du damit den Suchvorgang abrupt. Das zurückgeben von true sorgt logischerweise für das Fortsetzen des Suchvorgangs.
    */
    }
    
    ScanFoldersForFiles(STRING("C:\\*"), HandleFoundFiles, NULL);
    // Immer wenn er jetzt etwas findet, ruft er die Funktion 'HandleFoundFiles' auf.
    

    Da der Dir aber nun die ganze Festplatte *C:* nach Dateien durchsucht, ist es ratsam den ganzen Kram in einen Thread auszulagern und beim letzten Parameter (hier: NULL) beispielsweise ein Zeiger auf eine Struktur oder ein Flag (bool) zu übergeben, um diesen Thread dann bei Bedarf (beispielsweise bei einer bestimmten Datei oder einer gewissen Anzahl) abbrechen zu können (_endthreadex()), also eine Art Kontrollvariable für das Laufzeitverhalten des Threads 😉 .

    Hoffe das ist verständlich 👍 .

    PS @ All: Da gibts in der FAQ ja schon einen Eintrag zu, dieser wär vllt ne (gute ?! 🤡 ) Ergänzung, oder ?


Anmelden zum Antworten