Pobawiliśmy się już w zapisywanie i odczytywanie danych z i do pliku, teraz kolej przyszła pobuszować w katalogach i nauczyć się znajdowania plików i folderów zawartych w danej lokalizacji. Do tego celu można posłużyć się funkcjami _findfirst oraz _findnext, które umożliwiają przeszukiwanie danej ścieżki według podanego filtra przeszukiwania. Oto przykłady filtrów, wraz z ich opisem:
C:\* - wyszukiwanie wszystkich elementów zawartych w podanej lokalizacji;
C:\*.txt - wyszuka wszystkie pliki z rozszerzeniem txt;
C:\?1*.txt - wyszuka wszystkie pliki, które po pierwszym dowolnym znaku mają 1 w nazwie a za nią dowolną liczbę innych znaków i kończą się rozszerzeniem .txt (przykłady: "f1000.txt"; "11.txt" ...)
Oto przykładowy mały programik przeszukujący daną lokalizację oraz podfoldery i wyświetlająca katalogi i nazwy plików, w których dany plik został znaleziony:
#include <iostream>
#include <vector>
#include <string>
#include <windows.h>
#include <io.h>
using namespace std;
void searchFile(string path, string filtr){
_finddata_t f;
intptr_t r;
// przeszukiwanie podfolderów
string searching_filtr = path; // tworzę filtr przeszukiwania folderów
searching_filtr += "*"; // na końcu potrzebna jest *
if((r=_findfirst(searching_filtr.c_str(),&f))>0){ // znajdowanie pierwszego folderu (to zawsze jest .
while(!_findnext(r,&f)){
if(strcmp(f.name, "..")){ // drugi to ..
if(f.attrib & _A_SUBDIR){ // gdy dany element jest folderem to
string p2 = path; // kopię ścieżki głównej robię
p2 += f.name; // dodaję do tego nazwę folderu
p2 += char(92); // dodaję na końcu
searchFile(p2, filtr); // przeszukuję nową lokalizację
}
}
}
}
_findclose(r); // zamykam przeszukiwanie
// wyszukiwanie plików według podanego filtra
searching_filtr = path; // tworzę ścieżkę dla filtra
searching_filtr += filtr; // dodaję na koniec filtr
if((r=_findfirst(searching_filtr.c_str(),&f))>0){ // piersze przeszukiwanie
do{ // kolejne przeszukiwania
if(!(f.attrib & _A_SUBDIR)){ // jak nie jest to folder
cout<<path<<"t"<<f.name<<endl; // wypisuję nazwę znalezionego pliku
}
}while(!_findnext(r,&f));
}
_findclose(r); // zamykam przeszukiwanie
}
int main(int size, char** pt){
setlocale(LC_CTYPE,"Polish"); // polskie znaki
string path = pt[0]; // wyciągam ścieżkę z nazwą programu
string filtr = "*.*"; // to będzie filtr przeszukiwania
path.erase(path.begin() + path.find_last_of(char(92),string::npos) + 1,path.end()); // wydziabuję ścieżkę dostępu do lokalizacji programu
cout<<"Przeszukiwanie ścieżki: \apos"<<path<<"\apos z filtrem wyszukiwania \apos"<<filtr<<"\apos"<<endl<<endl; // wyświetlam
searchFile(path,filtr);
cout<<endl<<endl<<"Wciśnij enter, aby zamknąć program...";
cin.get();
return 0;
}
Warto przyjrzeć się nieco dokładniej strukturze _finddata_t, która ma następującą postać:
struct _finddata64i32_t {
unsigned attrib; // atrybuty pliku
__time64_t time_create; // czas utworzenia pliku
__time64_t time_access; // czas ostatniego dostępu do pliku
__time64_t time_write; // czas ostatniego nadpisania pliku
_fsize_t size; // rozmiar pliku
char name[260]; // nazwa pliku (maksymalnie 259 znaków bo jeden jest przeznaczony na NULL)
};
typedef _finddata64i32_t _finddata_t;
W zależności od systemu może stosowane są nieco inne deklaracje tej samej struktury, stąd też zapis na końcu typedef _finddata64i32_t _finddata_t;.
Warto przyjrzeć się polu struktury attrib, dla którego ustawienia bitów mówią o rodzaju i dostępie do danego pliku:
Ponieważ dany typ pliku czy folderu może mieć kilka różnych atrybutów, konieczne jest wykorzystanie operatora koniunkcji bitowej & (czyli AND). Dla folderów taki test będzie wyglądał następująco:
if(f.attrib & _A_SUBDIR){
cout<<"To jest folder!!!"<<endl;
}
Powyższe sprawdzenie zostało już oczywiście wykorzystane w programie.
Funkcje _findfirst i _findnext są nieco przestarzałe i nie obsługują szerokich znaków (typ wchar_t), co w konsekwencji może doprowadzić, że plik zawierający w nazwie znaki kodowania np. cyrylicy czy alfabetu greckiego nie będą poprawnie obsługiwane. Z tego względu warto też zwrócić uwagę na funkcje _wfindfirst oraz _wfindnext.