Narzędzia użytkownika

Narzędzia witryny


filesystem

Biblioteka boost::filesystem

Michał Wasiak, G1ISI

Wstęp

Biblioteka boost::filesystem wprowadza przenośne udogodnienia w zarządzaniu ścieżkami dostępu do katalogów i plików.

Cechy

  • Programy używające tej biblioteki charakteryzuje pełna przenośność. Składnia, jak i zachowanie kodu programu są całkowicie niezależne od systemu operacyjnego.
  • Użycie biblioteki uważa się za bezpieczne, ponieważ ewentualne błędy nie mogą zostać zignorowane, gdyż większość funkcji zwraca wyjątki przy napotkaniu błędów.

Pliki nagłówkowe

  • Nagłówek <boost/filesystem.hpp> wprowadza klasę basic_path i jej pochodne: path i wpath. Poza tym udostępnia wiele pomocnych operacji na plikach i katalogach. Klasa basic_directory_iterator umożliwia niezwykle użytczne iteracje po zawartości katalogów.
  • Nagłowek <boost/filesystem/fstream.hpp> wprowadza takie same elementy co nagłówek fstream standardowej biblioteki C++, lecz same pliki są identyfikowane przez obiekty basic_path, a nie char *s.

Podstawowe klasy

basic_path

Obiekt klasy path może być stworzony w następujący sposób:

path my_path("sciezka/plik.txt");

String podawany jak argument może być ścieżką w ogólnym przenośnym formacie lub w natywnym formacie systemu operacyjnego.

Klasa path posiada konstruktory konwersji z const char* i std::string&, więc pomimo tego, że biblioteka Filesystem używa parametru formalnego path& to i tak dozwolone jest podawanie jako parametrów napisów w stylu C:

remove_all("plik");
create_directory("teczka");
if(!exists("katalog/obraz"))
	std::cout << "Błędna ścieżka\n";

Ścieżki mogą zawierać odwołanie do bieżącego katalogu używając notacji „.”, a także do nadkatalogu, używając zapisu „..”.

basic_directory_iterator

Klasa basic_directory_iterator jest ważnym elementem biblioteki Filesystem. Wprowadza iterator wejściowy po zawartości katalogu z wartością typu basic_path. Jako przykład użycia tego iteratora przedstawiona zostanie funkcja, która rekursywnie wyszukuje plik o podanej nazwie w podanym katalogu i jego podkatalogach. Zwraca wartość bool i ewentualnie ścieżkę do odnalezionego pliku:

namespace bf = boost::filesystem;
 
bool find_file( const bf::path& dir_path,
                const std::string& file_name,
                path& path_found ) {
    if(!exists(dir_path) || !is_directory(dir_path))
        return false;
    directory_iterator iter(dir_path), end_iter;
    for(; iter != end_iterl ++iter) {
        if(bf::is_directory(*iter)) {
            if(find_file(*iter, file_name, path_found))
                return true;
	}
        else if(iter->leaf() == file_name) {
            path_found = *iter;
                return true;
        }
    }
    return false;
}

Funkcja najpierw sprawdza czy podana ścieżka w path_dir istnieje i jest katalogiem. Później tworzy obiekt directory_iterator podając ściężkę do konstruktora. Pętla iteruje po wszystkich elementach katalogu i dla każdego napotkanego podkatalogu wywołuje rekursywnie funkcję szukającą. Każdy rozpoznany plik jest porównywany co do nazwy z paramatrem file_name i jeśli znaleziony zostanie plik o podanej nazwie to do path_found kopiowana jest scieżka do tego pliku i zwracana jest wartość 'true'. Jeśli nic nie zostanie znalezione to zwrcana jest wartość 'false'.

Formaty ścieżek

Są dwa formaty łańcuchów opisujących ścieżkę dostępu:

  • Przenośny format opisany przez POSIX;
  • Natywny format ścieżki dostępu definiowany przez system operacyjny. Przykłady:
    OpenVMS: „SYS1::DISK1:[DANE.FILMY]”
    Windows: „c:\dane\filmy”

Pomocne funkcje

void copy_file(const Path1& from_fp, const Path2& to_fp);

Funkcja kopiuje zawartość pliku from_fp do pliku to_fp.

void rename(const Path1& from_p, const Path2& to_p);

Funkcja zmienia nazwę pliku from_fp na nową to_p.

bool remove(const Path& p);

Funkcja usuwa plik p, jeśli plik taki istnieje. Zwraca wartość wyrażenia exists(p).

bool create_directory(const Path& dp);

Funkcja tworzy katalog dp. Zwraca wartość true, jeśli katalog został utworzony, w przeciwnym razie zwraca wartość false.

uintmax_t file_size(const Path& p);

Funkcja zwraca rozmiar pliku p w bajtach.

std::time_t last_write_time(const Path& p);

Funkcja zwraca czas ostatniej modyfikacji elementu p.

void last_write_time(const Path& p, const std::time_t new_time);

Funkcja ustawia czas ostatniej modyfikacji pliku p na new_time.

template <class Path> typename Path::string_type extension(const Path & p);

Funkcja zwracająca rozszerzenie pliku. Jeśli p.leaf() zawiera kropkę, to funkcja zwraca podciąg z p.leaf() od ostatniej kropki aż do ostatniego znaku nazwy. W przeciwnym razie zwraca pusty łańcuch.

template <class Path> typename Path::string_type basename(const Path & p);

Funkcja zwracająca nazwę pliku bez rozszerzenia. Jeśli p.leaf() zawiera kropkę, to funkcja zwraca podciąg z p.leaf() od początku nazwy do ostatniej kropki (kropka nie jest zawarta). W przeciwnym wypadku zwraca p.leaf().

template <class Path> Path replace_extension(const Path & p, const typename Path::string_type & new_extension);

Funkcja zmienia rozszerzenie pliku p na nowe new_extension.

Przykład użycia

Prosty program wypisujący zawartość katalogu:

namespace bf = boost::filesystem;
 
void listing(const bf::path& p) {
    unsigned long dir_count = 0;
    unsigned long file_count = 0;
    unsigned long other_count = 0;
    unsigned long err_count = 0;
 
    if(!bf::exists(p)) {
        std::cout << "Nieprawidlowa sciezka: " << p.native_file_string() << std::endl;
            return;
    }
 
    if(bf::is_directory(p)) {
        std::cout << "W katalogu: " << p.native_file_string() << "\n\n";
        bf::directory_iterator iter(p), end_iter;
        for(; iter != end_iter; ++iter) {
            try {
                if(bf::is_directory(*iter)) {
                    ++dir_count;
                    std::cout << iter->leaf() << " [katalog]\n";
                }
                else if(fs::is_regular(iter->status())) {
                    ++file_count;
                    std::cout << iter->leaf() << " ("  << bf::file_size(*iter) <<" B)\n";
                }
                else {
                    ++other_count;
                    std::cout << iter->leaf() << " [inny]\n";
                }
            } catch(const std::exception& ex) {
                ++err_count;
                std::cout << iter->leaf() << ": " << ex.what() << std::endl;
            }	
        }
        std::cout << std::endl << file_count << " plikow\n"
                  << dir_count << " katalogow\n"
                  << other << " innych\n"
                  << err_count << " błedów\n";
    }
    else { // musi byc plikiem
        std::cout << "Plik: " << p.native_file_string() << std::endl;
    }
}

Uwagi

Przy całym dobrodziejstwie niesionym przez bibliotekę Filesystem trzeba także pamiętać o pewnych pułapkach czyhających na programistę. Otóż podczas wykonywania funkcji może wystąpić wyścig, a wtedy wartość zwracana przez funkcję może ulec zmianie do czasu powrotu do funkcji wywołującej.

Pliki lub katalogi są często współdzielone i przez to ich stan może zostać zmieniony prze inny wątek, proces, a nawet inny komputer posiadający sieciowy dostęp do systemu plików. Oto kilka przykładów pokazujących istotę problemu:

assert(exists("foo") == exists("foo"));

W tym przypadku może się zdarzyć, iż nieistniejący plik bądź katalog zostanie utworzony lub istniejący zostanie usunięty pomiędzy pierwszy a drugim wywołaniem exists(). Taka sytuacja może nastąpić, jeśli podczas wykonania przykładowego kodu inny wątek, proces lub komputer także operuje w tym samym folderze.

remove_all("foo");
assert(!exists("foo"));

Taka próba nie powiedzie się, jeśli między wywołaniem remove_all() a wywołaniem exists() inny wątek, proces lub komputer stworzy plik bądź katalog o nazwie „foo”.

assert(is_directory("foo") == is_directory("foo"));

Zawiedzie, w przypadku gdy inny wątek, proces badź też komputer pomiędzy dwoma wywołaniami is_directory() usunie istniejący plik „foo” i stworzy katalog o nazwie „foo”.

filesystem.txt · ostatnio zmienione: 2008/04/16 23:31 przez michal.wasiak