W niniejszym artykule opisano przenośną bibliotekę dirent.h od strony języka C i C++, która służy do listowania plików. Artykuł zawiera rozbudowane przykłady, prezentujące praktyczne zastosowanie biblioteki. (artykuł)
Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Zarejestruj się!
dział serwisuArtykuły
kategoriaInne artykuły
artykuł[C, C++] Listowanie plików - biblioteka dirent.h
Autor: Grzegorz 'baziorek' Bazior

[C, C++] Listowanie plików - biblioteka dirent.h

[artykuł] W niniejszym artykule opisano przenośną bibliotekę dirent.h od strony języka C i C++, która służy do listowania plików. Artykuł zawiera rozbudowane przykłady, prezentujące praktyczne zastosowanie biblioteki.

Intro - co, po co, dlaczego

Skąd pomysł na artykuł

Kiedyś zetknąłem się z problemem listowania plików i katalogów z poziomy języka C++, dowiedziałem się wówczas o pewnej bibliotece języka C: dirent.h, która to umożliwia.
Podjąłem decyzję o napisaniu tego artykułu, ponieważ wg Wikipedii jest ona "uważana za pseudo-standard", a co za tym idzie przenośna, będąc zaimplementowaną w m.in. poniższych kompilatorach:
  • Turbo C++ (DOS)
  • GCC (Cross-platform)
  • MinGW (Microsoft Windows version of GCC)
  • Borland C++ Builder (Microsoft Windows)
Nie jest standardowo w Microsoft Visual Studio, ale można pobrać darmową implementacją dla systemu Windows.

Jak korzystać z biblioteki

Opis funkcji

Czasami nie ma sensu dawać przykładu bez jakiejkolwiek teorii, wg mnie właśnie teraz jest taki czas, w związku z tym poniżej zamieszczam opis funkcji i typów z naszej biblioteki w oparciu o Wikipedię polską i angielską:
DIR - struktura reprezentująca strumień ścieżki. Nie jest zdefiniowana przez POSIX (ang. Portable Operating System Interface for Unix, na polski: przenośny interfejs dla systemu operacyjnego Unix)
struct dirent - struktura wskazująca na element w katalogu (plik/folder) zawierająca:
  ino_t d_ino - numer seryjny pliku
  char d_name[] - nazwa wejściowa (nie przekraczająca maksymalnej długości nazwy wyznaczonej przez NAME_MAX)
w niektórych implementacjach struktura może zawierać również:
off_t d_off - offset pliku
unsigned short int d_reclen - długość rekordu struktury
unsigned short int d_namlen - długość nazwy
unsigned int d_type - typ pliku
NAME_MAX (inaczej FILENAME_MAX) - maksymalna długość tablicy char d_name;

DIR* opendir(const char* nazwa_sciezki) - funkcja otwierająca strumień do katalogu, którego nazwa została podana jako argument. Jeżeli otwarcie się powiedzie funkcja zwróci wskaźnik do obiektu typu DIR, w innym przypadku zwróci NULL i zapisze informacje o błędzie w zmiennej errno. Strumień po otwarciu jest ustawiony na pierwszy element. Jeśli typ DIR jest zaimplementowany przy pomocy deskryptorów plików, program będzie w stanie otworzyć ilość plików i folderów nie większą niż OPEN_MAX.
Możliwe błędy w errno zapisane przez powyższą funkcję i ich znaczenie:
  • EACCES oznacza brak dostępu do czytania ścieżki,
  • ELOOP oznacza zbyt wiele symbolicznych powiązań napotkanych w ścieżce,
  • ENAMETOOLONG długość ścieżki przekracza PATH_MAX, lub długość nazw jej komponentów przekracza NAME_MAX,
  • ENOENT gdy ścieżka jest pustym stringiem, lub składnik ścieżki nie jest nazwą istniejącej ścieżki,
  • ENOTDIR nazwa ścieżki nie jest ścieżką.
  • EMFILE oznacza że jest otwarta liczba plików równa OPEN_MAX
  • ENAMETOOLONG oznacza że nazwa ścieżki połączenia symbolicznego przekracza długość PATH_MAX
  • ENFILE oznacza że zbyt wiele plików jest obecnie otwartych przez system

struct dirent* readdir(DIR* dirp) - funkcja zwracjąca wskaźnik do struktury reprezentującej plik/folder w katalogu i ustawiająca wskaźnik na następny element katalogu podanego jako argument tejże funkcji. Jeżeli wskaźnik dojdzie do końca struktury zostanie zwrócony NULL. Jeżeli istnieją elementy katalogu . (obecny katalog) i ..(nadrzędny katalog) to również zostaną one wyświetlone. Przy błędach zostanie zwrócony wskaźnik do NULL i w errno znajdzie się odpowiednia informacja. Użytkownik nie powinien samodzielnie zwalniać pamięci na którą pokazuje wskaźnik do struktury dirent.
Możliwe błędy w errno zapisane przez powyższą funkcję i ich znaczenie:
  • EOVERFLOW oznacza że jedna z wartości w strukturze nie może być zwrócona poprawnie
  • EBADF oznacza że argument tej funkcji nie odnosi się do otwartego strumienia ścieżki
  • ENOENT oznacza że obecna pozycja strumienia jest nieprawidłowa

int closedir(DIR* dirp) - zamyka strumień ścieżki podany jako argument funkcji. Jeśli zamknięcie przebiegnie bezbłędnie zwrócone zostanie 0, w przeciwnym wypadku -1, a w errno zostanie zawarta odpowiednia informacja jedna z poniższych:
  • EBADF oznacza że dirp nie odnosi się do otwartego strumienia ścieżki
  • EINTR pojawia się wtedy gry funkcja jest przerwana przez sygnał

Listowanie plików/folderów

Mamy już dużą część teorii, wystarczająco aby podać przykład na listowanie zawartości katalogów.
Zacznę od nagłówka, jeżeli nasz kompilator jest wyposażony w tą bibliotekę wystarczy zaincludować znanym nam sposobem bibliotekę:
#include <dirent.h>
, includujemy ją tak samo w C i C++.

Przykład na najzwyklejsze listowanie katalogów (w C)

C/C++
#include <dirent.h>
#include <stdio.h>

void listujPliki( const char * nazwa_sciezki ) {
    struct dirent * plik;
    DIR * sciezka;
   
    if(( sciezka = opendir( nazwa_sciezki ) ) ) {
        while(( plik = readdir( sciezka ) ) )
             puts( plik->d_name );
       
        closedir( sciezka );
    }
    else
         printf( "! wywołując funkcję opendir(%s) pojawił się błąd otwarcia strumienia dla danej ścieżki, może nie istnieje, lub podano ścieżkę pustą\n", nazwa_sciezki );
   
}

main( int argc, char ** argv ) {
    if( argc > 1 )
         listujPliki( argv[ 1 ] );
    else
         listujPliki( "." );
   
} //    gcc     listowanie.c -o listowanie && ./listowanie
Na wyjaśnienie zasługuje jedynie funkcja main - jeżeli podamy argument uruchomienia programu, wtedy argc będzie większy niż 1 i dla argumentu wywołania (będącego ścieżką w systemie plików) zostanie wypisana zawartość tej ścieżki. Gdy uruchomimy ten program bez argumentów zostanie wypisana zawartość katalogu z którego ów program został uruchomiony. W moim przypadku gdy uruchomię program gcc     listowanie.c -o listowanie && ./listowanie zostaną wyświetlone elementy:
  • tmp.cc
  • .
  • listowanie.c
  • tmp.c
  • ..
  • listowanie
  • tmp
  • PulpitWindows
  • program_erep
Jak uruchomię program gcc     listowanie.c -o listowanie && ./listowanie ., zostanie zwrócone to samo co powyżej. Mogę również podać ścieżkę bezwzględną jako argument programu, u mnie na Ubuntu będzie to wyglądało tak:
gcc     listowanie.c -o listowanie && ./listowanie ~/Pulpit i też zostanie zwrócony ten sam wynik co powyżej. Natomiast dla innych argumentów uruchamiania programu wyniki będą inne.

Ładniejsze listowanie plików/folderów (w C)

Jako że foldery często mają większą liczbę elementów, oraz jak widzieliśmy w poprzednim przykładzie zawartość danego katalogu jest niealfabetyczne, dlatego umieszczę tutaj kolejny, już bardziej skomplikowany, ale wyświetlający w ładniejszy sposób program, w którym ponadto uwzględniam errno i jej opis:
C/C++
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h> /* malloc */
#include <string.h> /* memset */
#include <errno.h>

int pokazErrno();

void listujPlikiLadnie( const char * nazwa_sciezki, unsigned ilosc_w_linii, unsigned rozmiar_bufora ) { // (1)
    unsigned licznik = 0, i;
    char * bufor =( char * ) malloc( sizeof( char ) * rozmiar_bufora );
   
    struct dirent * plik;
    DIR * sciezka;
   
    if(( sciezka = opendir( nazwa_sciezki ) ) ) {
        while(( plik = readdir( sciezka ) ) ) {
            memset( bufor, ' ', rozmiar_bufora );
            sprintf( bufor, "%s", plik->d_name );
            printf( "%d)\t", ++licznik );
           
            for( i = 0; i < rozmiar_bufora; ++i ) // (2)
                 printf( "%c", bufor[ i ] );
           
            printf( "\t" );
            if( !( licznik % ilosc_w_linii ) )
                 printf( "\n" );
           
        }
        pokazErrno();
        closedir( sciezka );
        pokazErrno();
        printf( "\n" );
    }
    else
         pokazErrno();
   
    free( bufor );
}

int pokazErrno() { // (3)
    int errno_kopia = errno;
    if( errno ) {
        printf( "errno = %d:\t", errno ); fflush( stdout ); // (4)
        switch( errno ) {
        case EACCES: perror( "! (problem w OPENDIR) brak dostepu do czytanej sciezki" ); break;
        case ELOOP: perror( "! (problem w OPENDIR) zbyt wiele symbolicznych powiązań napotkanych w ścieżce" ); break;
        case ENAMETOOLONG: perror( "! (problem w OPENDIR) długość ścieżki przekracza PATH_MAX, lub długość nazw jej komponentów przekracza NAME_MAX" ); break;
        case ENOENT: perror( "! (problem w OPENDIR) ścieżka jest pustym stringiem, lub (problem w OPENDIR lub READDIR) składnik ścieżki nie jest nazwą istniejącej ścieżki\n" ); break;
        case ENOTDIR: perror( "! (problem w OPENDIR) komponent lub nazwa ścieżki nie jest ścieżką" ); break;
        case EMFILE: perror( "! (problem w OPENDIR) jest otwarta liczba plików równa OPEN_MAX" ); break;
        case ENFILE: perror( "! (problem w OPENDIR) zbyt wiele plików jest obecnie otwartych przez system" ); break;
        case EOVERFLOW: perror( "! (problem w READDIR) jedna z wartości w strukturze nie może być zwrócona poprawnie" ); break;
        case EBADF: perror( "! (problem w READDIR lub CLOSEDIR) argument funkcji readdir nie odnosi się do otwartego strumienia ścieżki" ); break;
        case EINTR: perror( "! (problem w CLOSEDIR) funkcja jest przerwana przez sygnał" ); break;
        };
        errno = 0;
        printf( "\n" );
    }
    return errno_kopia;
}

main( int argc, char ** argv ) {
    if( argc > 1 )
         listujPlikiLadnie( argv[ 1 ], 4, 30 );
    else
         listujPlikiLadnie( ".", 4, 30 );
   
} //    gcc     listowanie2.c -o listowanie2 && ./listowanie2
Program drukuje u mnie następującą treść, w następujący sposób:

1)      listowanie3.cc~                 2)      tmp.cc                          3)      listowanie3                     4)      .                           
5)      listowanie2                     6)      listowanie2.c~                  7)      listowanie.c                    8)      tmp.c                       
9)      listowanie3.cc                  10)     ..                              11)     listowanie2.c                   12)     listowanie                  
13)     tmp                             14)     PulpitWindows                   15)     program_erep
Opis paru trudniejszych miejsc w programie:
  • 1
    void listujPlikiLadnie( const char * nazwa_sciezki, unsigned ilosc_w_linii, unsigned rozmiar_bufora )
    jest to funkcja, która oprócz ścieżki przyjmuje jako argumenty ilość nazw plików/folderów jaką chcemy wyświetlić w jednej linii, oraz rozmiar bufora - w nim jest zawarta informacja jaka szerokość nazwy jest przewidziana na nazwę wpisu
  • 2
    C/C++
    for( i = 0; i < rozmiar_bufora; ++i ) // (2)
         printf( "%c", bufor[ i ] );
    zapewne zastanawiasz się dlaczego wyświetlam całą tabelę znak po znaku zamiast po prostu napisać
    printf( "%s", bufor );
    , powód jest taki że chcę to wyświetlić tak aby zajmowało pewną, ustaloną szerokość, niezależnie od długości nazwy, da się to zrobić pisząc
    printf( "%30s", bufor );
    , ale wtedy wszystkie teksty będą przesunięte maksymalnie w prawo a mi osobiście coś takiego gorzej się przegląda
  • 3
    int pokazErrno()
    jest to funkcja, która gdy wartość errno będzie różna od 0, wyświetli informacje opisującą zaistniały błąd (o ile dotyczy on problemów używanych przeze mnie funkcji)
  • 4
    printf( "errno = %d:\t", errno ); fflush( stdout );
    ponieważ używam różnych strumieni (printf oraz perror) muszę wyczyścić bufor aby zostało wszystko wyświetlone w pożądanej kolejności

Alfabetyczne listowanie zawartości katalogu (już C++)

Zapewne dziwnym wydają się 3 różne funkcje robiące to samo, różniące się jedynie estetyką, robię to gdyż zależy mi na przejściu od prostoty do funkcjonalności. Poniższy program jest już napisany w C++ ze względu na duże ułatwienia w nim dostępne, mniejszą objętość kodu (i nie ukrywam że miałem problemy żeby coś takiego w pełni działało w C):
C/C++
#include <iostream>
#include <dirent.h>
#include <cstdio>
#include <cerrno>
#include <string>
#include <set>
#include <iomanip>
using namespace std;

int pokazErrno();

set < string > listujPlikiPosortowane( const char * nazwa_sciezki, unsigned ilosc_w_linii = 4, unsigned rozmiar_bufora = 30, bool zlicznie = true, ostream & strumien = cout ) { // (1)
    set < string > nazwy_plikow;
   
    struct dirent * plik;
    DIR * sciezka;
   
    if(( sciezka = opendir( nazwa_sciezki ) ) ) {
        pokazErrno();
       
        while(( plik = readdir( sciezka ) ) )
             nazwy_plikow.insert( plik->d_name );
       
        pokazErrno();
       
        closedir( sciezka );
        pokazErrno();
       
        unsigned licznik = 0;
        if( zlicznie )
        for( set < string >::iterator it = nazwy_plikow.begin(); it != nazwy_plikow.end(); ++it )
             strumien << ++licznik << ")\t" << setw( rozmiar_bufora ) << left << * it << '\t' <<(( !( licznik % ilosc_w_linii - 3 ) ) ? "\n"
            : "" );
        else
        for( set < string >::iterator it = nazwy_plikow.begin(); it != nazwy_plikow.end(); ++it )
             strumien << setw( rozmiar_bufora ) << left << * it << '\t' <<(( !( licznik % ilosc_w_linii - ilosc_w_linii + 1 ) ) ? "\n"
            : "" );
       
        strumien << endl;
    }
    else
         pokazErrno();
   
    return nazwy_plikow;
}

int pokazErrno() {
    int errno_kopia = errno;
    if( errno ) {
        printf( "errno = %d:\t", errno ); fflush( stdout );
        switch( errno ) {
        case EACCES: perror( "! (problem w OPENDIR) brak dostepu do czytanej sciezki" ); break;
        case ELOOP: perror( "! (problem w OPENDIR) zbyt wiele symbolicznych powiązań napotkanych w ścieżce" ); break;
        case ENAMETOOLONG: perror( "! (problem w OPENDIR) długość ścieżki przekracza PATH_MAX, lub długość nazw jej komponentów przekracza NAME_MAX" ); break;
        case ENOENT: perror( "! (problem w OPENDIR) ścieżka jest pustym stringiem, lub (problem w OPENDIR lub READDIR) składnik ścieżki nie jest nazwą istniejącej ścieżki\n" ); break;
        case ENOTDIR: perror( "! (problem w OPENDIR) komponent lub nazwa ścieżki nie jest ścieżką" ); break;
        case EMFILE: perror( "! (problem w OPENDIR) jest otwarta liczba plików równa OPEN_MAX" ); break;
        case ENFILE: perror( "! (problem w OPENDIR) zbyt wiele plików jest obecnie otwartych przez system" ); break;
        case EOVERFLOW: perror( "! (problem w READDIR) jedna z wartości w strukturze nie może być zwrócona poprawnie" ); break;
        case EBADF: perror( "! (problem w READDIR lub CLOSEDIR) argument funkcji readdir nie odnosi się do otwartego strumienia ścieżki" ); break;
        case EINTR: perror( "! (problem w CLOSEDIR) pojawia się wtedy gry funkcja jest przerwana przez sygnał" ); break;
        };
        errno = 0;
        printf( "\n" );
    }
    return errno_kopia;
}

main( int argc, char ** argv ) {
    ios_base::sync_with_stdio( 0 ); // (2)
    if( argc > 1 )
         listujPlikiPosortowane( argv[ 1 ] );
    else
         listujPlikiPosortowane( "." );
   
    cout << "\nj.w. ale w linii, bez liczenia:\n";
    listujPlikiPosortowane( ".", 1, 50, false );
} //    g++     listowanie3.cc -o listowanie3 && ./listowanie3
Wydruk programu:

1)      .                               2)      ..                              3)      PulpitWindows                   4)      listowanie                   
5)      listowanie.c                    6)      listowanie2                     7)      listowanie2.c                   8)      listowanie3                  
9)      listowanie3.cc                  10)     program_erep                    11)     tmp                             12)     tmp.c                        
13)     tmp.cc                       

j.w. ale w linii, bez liczenia:
.                                                
..                                               
PulpitWindows                                    
listowanie                                       
listowanie.c                                     
listowanie2                                      
listowanie2.c                                    
listowanie3                                      
listowanie3.cc                                   
program_erep                                     
tmp                                              
tmp.c                                            
tmp.cc
Oraz oczywiście czas na opis trudniejszych/ciekawszych miejsc w programie:
  • 1
    set < string > listujPlikiPosortowane( const char * nazwa_sciezki, unsigned ilosc_w_linii = 4, unsigned rozmiar_bufora = 30, bool zlicznie = true, ostream & strumien = cout )
    funkcja wypisująca zawartość katalogu o nazwie/ścieżce podanej w jej pierwszym argumencie, funkcja poza ilością w linii i rozmiarem bufora, które teraz mają wartości domyślne, może przyjąć jeszcze 2 argumenty: pierwszy decyduje czy każda z pozycji w danym katalogu będzie poprzedzona odpowiednim numerkiem, drugi jest strumieniem wyjściowym, może to być m.in. strumień piszący do pliku
  • 2
    ios_base::sync_with_stdio( 0 );
    w związku z tym że używam wielu strumieni na raz, potrzebuję, a raczej wypadałoby, zsynchronizować je ze sobą aby kolejność wyświetlania była taka jak zdefiniujemy, do tego właśnie służy ta funkcja

Dalsze możliwości biblioteki -przesuwanie wskaźnika do katalogu

Mamy już podstawy do używania naszego dirent.h, ale warto poznać jej dodatkowe funkcje, zacznę od ich opisu:

void rewinddir(DIR* dirp) - ustawia DIR* dirp na pierwszy element danego katalogu, jeżeli dirp nie wskazuje na strumień katalogowy efekt jest niezdefiniowany.

void seekdir(DIR* dirp, long int loc) - ustawia pozycję strumienia katalogowego w taki sposób, że następne użycie readdir() wskaże na strumień podany w argumencie loc. Wartość argumentu loc powinna być zwrócona przez użytą wcześniej funkcję telldir(), jeżeli nie zostanie efekt może być nieokreślony. Efekt nieokreślony pojawi się również w sytuacji jeżeli między telldir() a seekdir() zostanie wywołana funkcja rewinddir().

long int telldir(DIR* dirp) - pozyskuje wartość liczbową powiązaną z aktualnie wskazywaną pozycją w strumieniu katalogowym.

int readdir_r(DIR* dirp, struct dirent* entry, struct dirent** result) - inicjalizuje entry aby wskazywało na następny składnik strumienia katalogowego dirp, pod zmienną result jest zapisywany wskaźnik do tego składnika. Gdy funkcja zostanie wykonana z powodzeniem result będzie miał taką samą wartość co entry, natomiast w przypadku dojścia do końca strumienia katalogowego wskaźnik będzie wynosił NULL.
Funkcja wydaje się być dziwna, natomiast plotki anglojęzycznego internetu głoszą że jest bezpieczniejsza choćby przez to że nie zwróci elementu o pustej nazwie, ja jednak wolę najzwyklejsze readdir()
Błędy errno w przypadku niepowodzenia powyższej funkcji:
EBADF oznacza że pierwszy argument nie odnosi się do strumienia katalogowego

Program z readdir() i readdir_r(), oraz parametry plików

Przydałoby się pokazanie przykładu z funkcją readdir_r(), fajnie byłoby również pokazać parę parametrów dotyczących plików ponad samą ich nazwę, w związku z tym załączam przykładowy kod, w którym to równocześnie otwieram ścieżkę funkcją readdir() oraz readdir_r() i równocześnie przechodzę po całym katalogu wyświetlając pewne informacje dla plików (nie ma znaczenia przy pomocy której readdir() otrzymamy do nich wskaźnik):
C/C++
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

const char * typPliku( unsigned typ ) {
    switch( typ ) {
    case DT_UNKNOWN: return "Nieznany typ, nie wszystkie systemy rozróżniaja typy, czesto jest zwracana ta wartosc";
    case DT_REG: return "Regularny plik";
    case DT_DIR: return "Katalog";
    case DT_FIFO: return "Potok nazwany";
    case DT_SOCK: return "Lokalne gniazdo";
    case DT_CHR: return "Urządzenie znakowe 'A character device'";
    case DT_BLK: return "Urzadzenie blokowe 'A block device'";
    case DT_LNK: return "Dowiazanie symboliczne";
    };
}

void wyswietlDwojako( const char * nazwa_sciezki ) {
    int kontrolny_INT;
    DIR * sciezka1, * sciezka2;
    struct dirent element;
    struct dirent * element_wskaznik, * element_wskaznik2;
   
    if(( sciezka1 = opendir( nazwa_sciezki ) ) &&( sciezka2 = opendir( nazwa_sciezki ) ) ) {
        for( kontrolny_INT = readdir_r( sciezka1, & element, & element_wskaznik ), element_wskaznik2 = readdir( sciezka2 ); element_wskaznik != NULL && kontrolny_INT == 0 && sciezka2 != NULL; kontrolny_INT = readdir_r( sciezka1, & element, & element_wskaznik ), element_wskaznik2 = readdir( sciezka2 ) ) {
            printf( "readdir_r zwraca: normalnie = '%s' wskaznikowo = '%s'\n", element.d_name, element_wskaznik->d_name );
            printf( "readdir zwraca wskaznikowo = '%s'\n", element_wskaznik2->d_name );
            printf( "\tinne cechy pliku (nie na wszystkich systemach wszystkie będą działać):" );
            printf( "\tnumer seryjny pliku = %lu\n", element_wskaznik2->d_ino );
            printf( "\ttyp pliku nr %d, tekstowo: '%s'\n", element_wskaznik2->d_type, typPliku( element_wskaznik2->d_type ) );
            printf( "\toffset pliku = %ld", element_wskaznik2->d_off );
            printf( "\trozmiar struktury = %d\n", element_wskaznik2->d_reclen );
        }
        if( kontrolny_INT )
             perror( "blad uzycia funkcji readdir_r()" );
       
        closedir( sciezka1 );
        closedir( sciezka2 );
    }
    else
         perror( "blad przy otwarciu, chyba zla nazwa sciezki\n" );
   
}

main() {
    wyswietlDwojako( "." );
    printf( "\nmaksymalna dlugosc nazwy w d_name to %d\n", NAME_MAX );
} //    gcc     listowanie4.c -o listowanie4 && ./listowanie4
Program wydrukuje:

readdir_r zwraca: normalnie = 'tmp.cc' wskaznikowo = 'tmp.cc'
readdir zwraca wskaznikowo = 'tmp.cc'
        inne cechy pliku (nie na wszystkich systemach wszystkie będą działać):  numer seryjny pliku = 655831
        typ pliku nr 8, tekstowo: 'Regularny plik'
        offset pliku = 258636953        rozmiar struktury = 20
readdir_r zwraca: normalnie = 'listowanie3' wskaznikowo = 'listowanie3'
readdir zwraca wskaznikowo = 'listowanie3'
        inne cechy pliku (nie na wszystkich systemach wszystkie będą działać):  numer seryjny pliku = 655813
        typ pliku nr 8, tekstowo: 'Regularny plik'
        offset pliku = 326920626        rozmiar struktury = 24
readdir_r zwraca: normalnie = '.' wskaznikowo = '.'
readdir zwraca wskaznikowo = '.'
        inne cechy pliku (nie na wszystkich systemach wszystkie będą działać):  numer seryjny pliku = 666839
        typ pliku nr 4, tekstowo: 'Katalog'
        offset pliku = 339971867        rozmiar struktury = 16
readdir_r zwraca: normalnie = 'listowanie2' wskaznikowo = 'listowanie2'
readdir zwraca wskaznikowo = 'listowanie2'
        inne cechy pliku (nie na wszystkich systemach wszystkie będą działać):  numer seryjny pliku = 655792
        typ pliku nr 8, tekstowo: 'Regularny plik'
        offset pliku = 906211915        rozmiar struktury = 24
readdir_r zwraca: normalnie = 'listowanie.c' wskaznikowo = 'listowanie.c'
readdir zwraca wskaznikowo = 'listowanie.c'
        inne cechy pliku (nie na wszystkich systemach wszystkie będą działać):  numer seryjny pliku = 655785
        typ pliku nr 8, tekstowo: 'Regularny plik'
        offset pliku = 997644927        rozmiar struktury = 24
readdir_r zwraca: normalnie = 'tmp.c' wskaznikowo = 'tmp.c'
readdir zwraca wskaznikowo = 'tmp.c'
        inne cechy pliku (nie na wszystkich systemach wszystkie będą działać):  numer seryjny pliku = 655772
        typ pliku nr 8, tekstowo: 'Regularny plik'
        offset pliku = 1016279601       rozmiar struktury = 20
readdir_r zwraca: normalnie = 'listowanie5' wskaznikowo = 'listowanie5'
readdir zwraca wskaznikowo = 'listowanie5'
        inne cechy pliku (nie na wszystkich systemach wszystkie będą działać):  numer seryjny pliku = 656108
        typ pliku nr 8, tekstowo: 'Regularny plik'
        offset pliku = 1128728806       rozmiar struktury = 24
readdir_r zwraca: normalnie = 'listowanie3.cc' wskaznikowo = 'listowanie3.cc'
readdir zwraca wskaznikowo = 'listowanie3.cc'
        inne cechy pliku (nie na wszystkich systemach wszystkie będą działać):  numer seryjny pliku = 655847
        typ pliku nr 8, tekstowo: 'Regularny plik'
        offset pliku = 1382059105       rozmiar struktury = 28
readdir_r zwraca: normalnie = '..' wskaznikowo = '..'
readdir zwraca wskaznikowo = '..'
        inne cechy pliku (nie na wszystkich systemach wszystkie będą działać):  numer seryjny pliku = 660297
        typ pliku nr 4, tekstowo: 'Katalog'
        offset pliku = 1389163933       rozmiar struktury = 16
readdir_r zwraca: normalnie = 'listowanie2.c' wskaznikowo = 'listowanie2.c'
readdir zwraca wskaznikowo = 'listowanie2.c'
        inne cechy pliku (nie na wszystkich systemach wszystkie będą działać):  numer seryjny pliku = 655703
        typ pliku nr 8, tekstowo: 'Regularny plik'
        offset pliku = 1418850791       rozmiar struktury = 28
readdir_r zwraca: normalnie = 'listowanie' wskaznikowo = 'listowanie'
readdir zwraca wskaznikowo = 'listowanie'
        inne cechy pliku (nie na wszystkich systemach wszystkie będą działać):  numer seryjny pliku = 655475
        typ pliku nr 8, tekstowo: 'Regularny plik'
        offset pliku = 1509541328       rozmiar struktury = 24
readdir_r zwraca: normalnie = 'listowanie5.c' wskaznikowo = 'listowanie5.c'
readdir zwraca wskaznikowo = 'listowanie5.c'
        inne cechy pliku (nie na wszystkich systemach wszystkie będą działać):  numer seryjny pliku = 671246
        typ pliku nr 8, tekstowo: 'Regularny plik'
        offset pliku = 1546854446       rozmiar struktury = 28
readdir_r zwraca: normalnie = 'tmp' wskaznikowo = 'tmp'
readdir zwraca wskaznikowo = 'tmp'
        inne cechy pliku (nie na wszystkich systemach wszystkie będą działać):  numer seryjny pliku = 655393
        typ pliku nr 8, tekstowo: 'Regularny plik'
        offset pliku = 1738627556       rozmiar struktury = 16
readdir_r zwraca: normalnie = 'listowanie4' wskaznikowo = 'listowanie4'
readdir zwraca wskaznikowo = 'listowanie4'
        inne cechy pliku (nie na wszystkich systemach wszystkie będą działać):  numer seryjny pliku = 655442
        typ pliku nr 8, tekstowo: 'Regularny plik'
        offset pliku = 1883237439       rozmiar struktury = 24
readdir_r zwraca: normalnie = 'listowanie4.c' wskaznikowo = 'listowanie4.c'
readdir zwraca wskaznikowo = 'listowanie4.c'
        inne cechy pliku (nie na wszystkich systemach wszystkie będą działać):  numer seryjny pliku = 655667
        typ pliku nr 8, tekstowo: 'Regularny plik'
        offset pliku = 1941726828       rozmiar struktury = 28
readdir_r zwraca: normalnie = 'PulpitWindows' wskaznikowo = 'PulpitWindows'
readdir zwraca wskaznikowo = 'PulpitWindows'
        inne cechy pliku (nie na wszystkich systemach wszystkie będą działać):  numer seryjny pliku = 669527
        typ pliku nr 10, tekstowo: 'Dowiazanie symboliczne'
        offset pliku = 1988054319       rozmiar struktury = 28
readdir_r zwraca: normalnie = 'program_erep' wskaznikowo = 'program_erep'
readdir zwraca wskaznikowo = 'program_erep'
        inne cechy pliku (nie na wszystkich systemach wszystkie będą działać):  numer seryjny pliku = 969183
        typ pliku nr 4, tekstowo: 'Katalog'
        offset pliku = 2147483647       rozmiar struktury = 24

maksymalna dlugosc nazwy w d_name to 255

Funkcja scandir, czyli filtrowanie wraz z sortowaniem zawartoci katalogu

W bibliotece dirent.h okazały się znajdować jeszcze kolejne, fajne rzeczy, o których dowiedziałem się na pewnej stronie, a mianowicie:
int scandir( const char *dirp, struct dirent ***namelist, int (*filter)(const struct dirent *),
  int (*compar)(const struct dirent **, const struct dirent **));
- ta funkcja przyjmuje za pierwszy argument tekstowy adres ścieżki, za drugi przyjmuje dowolny struct dirent ** cos który sama osobiście zapełnia adresami egzemplarzy struktury dirent, ich ilość jest znana poprzez liczbę int zwróconą przez tą funkcję. Trzecim argumentem funkcji jest wskaźnik do odpowiedniej funkcji filtrującej, lub 0 gdy nie życzymy sobie filtracji, gdy funkcja zwróci wartość różną od 0 wtedy dany plik jest przepuszczany przez filtr, czyli dodawany do tablicy naszych zainteresowań. Ostatni argument to wskaźnik do funkcji porównującej; standardowo możemy użyć już gotowej funkcji z biblioteki:
int alphasort( const void * a, const void * b );
 sortującej alfabetycznie.
Poniżej zamieszczam przykładowy kod wykorzystujący te funkcje. Stworzony przeze mnie filtr akceptuje tylko pliki regulare zawierające w nazwie ".c".
C/C++
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

const char * typPliku( unsigned typ ) {
    switch( typ ) {
    case DT_UNKNOWN: return "Nieznany typ, nie wszystkie systemy rozróżniaja typy, czesto jest zwracana ta wartosc";
    case DT_REG: return "Regularny plik";
    case DT_DIR: return "Katalog";
    case DT_FIFO: return "Potok nazwany";
    case DT_SOCK: return "Lokalne gniazdo";
    case DT_CHR: return "Urządzenie znakowe 'A character device'";
    case DT_BLK: return "Urzadzenie blokowe 'A block device'";
    case DT_LNK: return "Dowiazanie symboliczne";
    };
}

int moj_filter( const struct dirent * plik ) {
    printf( "Filtruje nazwe: %s\t", plik->d_name );
    if( strstr( plik->d_name, ".c" ) == NULL ) {
        printf( "to nie plik .c ani .cc\n" );
        return 0;
    }
    if( plik->d_type != DT_REG ) {
        printf( "to nie jest zwykly plik\n" );
        return 0;
    }
    printf( " <- jest OK\n" );
    return 1;
}

int pokazChciane( const char * nazwa_sciezki, int( * filtr )( const struct dirent * ), int( * porownywanie )( const struct dirent **, const struct dirent ** ) ) {
    struct dirent ** sciezka;
    int i, ilosc_elementow = scandir( ".", & sciezka, filtr, porownywanie );
   
    printf( "\nilosc elementow w katalogu = %d\n", ilosc_elementow );
    for( i = 0; i < ilosc_elementow; ++i ) {
        printf( "%s\ttypu: '%s'\n", sciezka[ i ]->d_name, typPliku( sciezka[ i ]->d_type ) );
        free( sciezka[ i ] );
    }
    free( sciezka );
    printf( "\n" );
    return ilosc_elementow;
}

main() {
    pokazChciane( ".", moj_filter, alphasort );
} //    gcc     listowanie5.c -o listowanie5 && ./listowanie5
I wydruk, który jak widać jest taki jak chcieliśmy:

Filtruje nazwe: listowanie6wglab        to nie plik .c ani .cc
Filtruje nazwe: tmp.cc   <- jest OK
Filtruje nazwe: plikowanie.hpp  to nie plik .c ani .cc
Filtruje nazwe: .       to nie plik .c ani .cc
Filtruje nazwe: listowanie6wglab.cc      <- jest OK
Filtruje nazwe: listowanie.c     <- jest OK
Filtruje nazwe: tmp.c    <- jest OK
Filtruje nazwe: listowanie5     to nie plik .c ani .cc
Filtruje nazwe: listowanie3.cc   <- jest OK
Filtruje nazwe: ..      to nie plik .c ani .cc
Filtruje nazwe: listowanie2.c    <- jest OK
Filtruje nazwe: listowanie5.c    <- jest OK
Filtruje nazwe: listowanie4.c    <- jest OK
Filtruje nazwe: PulpitWindows   to nie plik .c ani .cc
Filtruje nazwe: program_erep    to nie plik .c ani .cc

ilosc elementow w katalogu = 8
listowanie.c    typu: 'Regularny plik'
listowanie2.c   typu: 'Regularny plik'
listowanie3.cc  typu: 'Regularny plik'
listowanie4.c   typu: 'Regularny plik'
listowanie5.c   typu: 'Regularny plik'
listowanie6wglab.cc     typu: 'Regularny plik'
tmp.c   typu: 'Regularny plik'
tmp.cc  typu: 'Regularny plik'


Wykorzystanie biblioteki do wyższych celów podczas operacji na drzewie katalogów/plików

Najwyższy czas na robienie czegoś więcej niż tylko wyświetlanie zawartości jednego folderu. Napisałem parę klas wykorzystujących wzorzec projektowy INTERPRETER, umożliwiających m.in.:
  • Wyświetlanie całego drzewa katalogów, z odpowiednimi, ustawianymi wcięciami, na wybrany strumień wyjściowy, z możliwością zastosowania odpowiedniego filtra dla plików (ale nie katalogów),
  • Usuwanie z drzewa katalogowego plików zawierających w nazwie dany ciąg tekstowy,
  • Zmiana danego podciągu w nazwach plików na inny,
  • Wreszcie wyszukiwanie w danym drzewie katalogowym plików zawierających określoną treść w którejś z linii.

Mini-biblioteka do operacji na drzewie katalogów

A poniżej kod umożliwiający ww. funkcjonalność, jeszcze niżej opis trudniejszych/ciekawszych miejsc w programie, w następnej kolejności przykłady jak z tego korzystać.
C/C++
#ifndef __PLIKOWANIE__HPP
#define __PLIKOWANIE__HPP

#include <iostream>
#include <iomanip>
#include <dirent.h>
#include <cstdio>
#include <cerrno>
#include <cstring>
#include <string>
#include <fstream>
#include <list>
#include <boost/ptr_container/ptr_set.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/tuple/tuple.hpp>
using namespace std;
using namespace boost;

const char * typPliku( unsigned typ ) { // (1)
    switch( typ ) {
    case DT_UNKNOWN: return "Nieznany typ, nie wszystkie systemy rozróżniaja typy, czesto jest zwracana ta wartosc";
    case DT_REG: return "Regularny plik";
    case DT_DIR: return "Katalog";
    case DT_FIFO: return "Potok nazwany";
    case DT_SOCK: return "Lokalne gniazdo";
    case DT_CHR: return "Urządzenie znakowe 'A character device'";
    case DT_BLK: return "Urzadzenie blokowe 'A block device'";
    case DT_LNK: return "Dowiazanie symboliczne";
    };
}

int pokazErrno() {
    int errno_kopia = errno;
    if( errno ) {
        printf( "errno = %d:\t", errno ); fflush( stdout );
        switch( errno ) {
        case EACCES: perror( "! (problem w OPENDIR) brak dostepu do czytanej sciezki" ); break;
        case ELOOP: perror( "! (problem w OPENDIR) zbyt wiele symbolicznych powiązań napotkanych w ścieżce" ); break;
        case ENAMETOOLONG: perror( "! (problem w OPENDIR) długość ścieżki przekracza PATH_MAX, lub długość nazw jej komponentów przekracza NAME_MAX" ); break;
        case ENOENT: perror( "! (problem w OPENDIR) ścieżka jest pustym stringiem, lub (problem w OPENDIR lub READDIR) składnik ścieżki nie jest nazwą istniejącej ścieżki\n" ); break;
        case ENOTDIR: perror( "! (problem w OPENDIR) komponent lub nazwa ścieżki nie jest ścieżką" ); break;
        case EMFILE: perror( "! (problem w OPENDIR) jest otwarta liczba plików równa OPEN_MAX" ); break;
        case ENFILE: perror( "! (problem w OPENDIR) zbyt wiele plików jest obecnie otwartych przez system" ); break;
        case EOVERFLOW: perror( "! (problem w READDIR) jedna z wartości w strukturze nie może być zwrócona poprawnie" ); break;
        case EBADF: perror( "! (problem w READDIR lub CLOSEDIR) argument funkcji readdir nie odnosi się do otwartego strumienia ścieżki" ); break;
        case EINTR: perror( "! (problem w CLOSEDIR) pojawia się wtedy gry funkcja jest przerwana przez sygnał" ); break;
        };
        errno = 0;
        printf( "\n" );
    }
    return errno_kopia;
}

struct Element {
    string adres;
    string nazwa;
    int nr_seryjny;
    unsigned typ;
   
    Element( string adress, string nazwa, int nr_seryjny, unsigned typ )
        : adres( adress )
        , nazwa( nazwa )
        , nr_seryjny( nr_seryjny )
        , typ( typ )
    {
        trim_left_if( adres, is_any_of( "." ) ); // (2)
        trim_left_if( nazwa, is_any_of( "." ) );
    }
   
    virtual ostream & drukuj( int( * filtr )( const Element & ) = NULL, unsigned glebokosc = 0, ostream & os = cout, bool nazwa_i_adres = true, unsigned dlugosc_nazwy = 30 ) {
        if( filtr == NULL ) { // (3)
            string tmp( glebokosc, '\t' );
            os << tmp;
            if( nazwa_i_adres )
                 os << setw( dlugosc_nazwy ) << left << nazwa << '\t' << adres << '\n';
            else
                 os << adres << '\n';
           
        }
        else if( filtr( * this ) ) {
            string tmp( glebokosc, '\t' );
            os << tmp;
            if( nazwa_i_adres )
                 os << setw( dlugosc_nazwy ) << left << nazwa << '\t' << adres << '\n';
            else
                 os << adres << '\n';
           
        }
        return os;
    }
   
    virtual unsigned usunZawierajaceWNazwie( string nazwa ) {
        return 0;
    }
   
    virtual unsigned przeliczElementy() {
        return 1;
    }
   
    virtual list < tuple < string, string, string > > plikiZawierajaceWTresci( string tresc ) = 0; // (4)
   
    virtual unsigned long long rozmiar( int( * filtr )( const Element & ) = NULL ) = 0;
   
    virtual unsigned zmienNazwyZawierajace( string szukany, string docelowy = "", int( * filtr )( const Element & ) = NULL ) {
        return 0;
    }
   
    virtual long unsigned drzewoKatalogowDoPliku( ostream & strumien, int( * filtr )( const Element & ) = NULL, string odstep = "\n", bool chce_nazwe_adres = true ) = 0;
};

inline bool operator <( const Element & l, const Element & r ) {
    return l.nazwa < r.nazwa;
}

struct Plik
    : public Element
{
    Plik( string adres, string nazwa, int nr_seryjny, unsigned typ )
        : Element( adres, nazwa, nr_seryjny, typ )
    { }
   
    virtual unsigned usunZawierajaceWNazwie( string fragment_nazwy ) { // (5)
        if( fragment_nazwy.empty() )
             cout << "Nie podano nazwy, usunal bys wszystko!" << endl;
        else if( nazwa.find( fragment_nazwy ) != string::npos ) {
            trim_left_if( adres, is_any_of( "/" ) );
            return( !remove( adres.c_str() ) );
        }
        return 0;
    }
   
    virtual list < tuple < string, string, string > > plikiZawierajaceWTresci( string tresc ) { // (6)
        list < tuple < string, string, string > > out;
        if( typ == DT_REG ) {
            string tmp;
            trim_left_if( adres, is_any_of( "/" ) );
           
            ifstream plik( adres.c_str() );
           
            if( plik.bad() ) {
                throw string( "Plik nie istnieje lub nie masz do niego praw!" );
            }
            while( !plik.eof() ) {
                getline( plik, tmp );
                if( tmp.find( tresc ) != string::npos ) { // (7)
                    out.push_back( make_tuple( nazwa, adres, tmp ) );
                    break;
                }
            }
        }
        return out;
    }
   
    virtual unsigned long long rozmiar( int( * filtr )( const Element & ) = NULL ) { // (8)
        unsigned long long roz = nazwa.size();
        if( typ == DT_REG ) {
            if( filtr == NULL ) {
                ifstream p( adres.c_str() );
                if( p.bad() )
                     cout << "!!! Nie mozna otworzyc pliku\n";
                else
                     roz +=( p.seekg( 0, ios::end ).tellg() );
               
            }
            else if( filtr( * this ) ) {
                ifstream p( adres.c_str() );
                if( p.bad() )
                     cout << "!!! Nie mozna otworzyc pliku\n";
                else
                     roz +=( p.seekg( 0, ios::end ).tellg() );
               
            }
        }
        return roz;
    }
   
    virtual unsigned zmienNazwyZawierajace( string szukany, string docelowy = "", int( * filtr )( const Element & ) = NULL ) { // (9)
        if( szukany.empty() )
             cout << "Nie podano nazwy, wyczyscil bys wszystkie nazwy!" << endl;
        else if( nazwa.find( szukany ) != string::npos ) {
            if( filtr == NULL ) {
                trim_left_if( adres, is_any_of( "/" ) );
                string obecny_adres = adres;
                replace_first( nazwa, szukany, docelowy );
                replace_first( adres, szukany, docelowy );
                return !rename( obecny_adres.c_str(), adres.c_str() );
            }
            else if( filtr( * this ) ) {
                trim_left_if( adres, is_any_of( "/" ) );
                string obecny_adres = adres;
                replace_first( adres, szukany, docelowy );
                return !rename( obecny_adres.c_str(), adres.c_str() );
            }
        }
        return 0;
    }
   
    virtual long unsigned drzewoKatalogowDoPliku( ostream & strumien, int( * filtr )( const Element & ) = NULL, string odstep = "\n", bool chce_nazwe_adres = true ) {
        if( typ == DT_REG ) {
            if(( filtr == NULL ) ? true
                : filtr( * this ) )
            { // (10)
                trim_left_if( adres, is_any_of( "/" ) );
                ifstream plik( adres.c_str() );
                if( plik.bad() )
                     cout << "!!! Nie mozna otworzyc pliku\n";
                else {
                    string tmp;
                    if( chce_nazwe_adres )
                         strumien << '\t' << nazwa << '\t' << adres << ":\n";
                   
                    while( !plik.eof() ) {
                        getline( plik, tmp );
                        strumien << tmp << '\n';
                    }
                    strumien << odstep;
                    return 1;
                }
            }
        }
        return 0;
    }
   
};

struct Katalog
    : public Element
{
    ptr_set < Element > elementy;
    DIR * sciezka;
   
    Katalog( string adres, string nazwa = "", int nr_seryjny = 0, unsigned typ = DT_DIR )
        : Element( adres, nazwa, nr_seryjny, typ )
    {
        sciezka = NULL;
        reorganizujSciezke( adres );
    }
   
    virtual list < tuple < string, string, string > > plikiZawierajaceWTresci( string tresc ) {
        list < tuple < string, string, string > > out, tmp;
        string nazwy, adresy, linijki;
       
        for( ptr_set < Element >::iterator it = elementy.begin(); it != elementy.end(); ++it ) {
            tmp = it->plikiZawierajaceWTresci( tresc );
            out.insert( out.end(), tmp.begin(), tmp.end() ); // (11)
        }
        return out;
    }
   
    static void wyswietlTuplePlikow( list < tuple < string, string, string > > pliki, ostream & os = cout, string szukana = "" ) { // (12)
        unsigned long licznik = 0;
        os << "W sumie znaleziono " << pliki.size() << " plikow zawierajacych szukany tekst " << szukana << '\n';
        for( list < tuple < string, string, string > >::iterator it = pliki.begin(); it != pliki.end(); ++it ) {
            cout << '\n' << ++licznik << "\tPLIK: '" << it->get < 0 >() << "' LEZACY POD ADRESEM: '" << it->get < 1 >() << "' MIESCI FRAZE W LINIJCE:\n";
            cout << it->get < 2 >() << '\n';
        }
    }
   
    void reorganizujSciezke( string & adres ) {
        struct dirent * plik;
       
        string tmp_adres = adres;
        elementy.clear();
       
        if( tmp_adres.empty() )
             tmp_adres = ".";
       
        trim_left_if( tmp_adres, is_any_of( "/" ) ); // (13)
       
        if( sciezka != NULL )
             closedir( sciezka );
       
        if(( sciezka = opendir( tmp_adres.c_str() ) ) == NULL )
             pokazErrno();
        else {
            while( plik = readdir( sciezka ) ) {
                string adres_obecnego_pliku =( tmp_adres + "/" + plik->d_name );
                if( !strcmp( plik->d_name, "." ) ) {
                }
                else if( !strcmp( plik->d_name, ".." ) ) {
                }
                else if( jestKatalogiek( plik ) )
                     elementy.insert( new Katalog( adres_obecnego_pliku, plik->d_name, plik->d_ino, plik->d_type ) );
                else
                     elementy.insert( new Plik( adres_obecnego_pliku, plik->d_name, plik->d_ino, plik->d_type ) );
               
            }
        }
        closedir( sciezka ); // (14)
        sciezka = NULL;
    }
   
    virtual unsigned przeliczElementy() {
        unsigned suma = 0;
        for( ptr_set < Element >::iterator it = elementy.begin(); it != elementy.end(); ++it )
             suma += it->przeliczElementy();
       
        return suma;
    }
   
    virtual unsigned usunZawierajaceWNazwie( string nazwa ) { // (15)
        unsigned licznik = 0;
        for( ptr_set < Element >::iterator it = elementy.begin(); it != elementy.end(); ++it )
             licznik += it->usunZawierajaceWNazwie( nazwa );
       
        if( licznik )
             reorganizujSciezke( adres );
       
        return licznik;
    }
   
    virtual ostream & drukuj( int( * filtr )( const Element & ) = NULL, unsigned glebokosc = 0, ostream & os = cout, bool nazwa_i_adres = true, unsigned dlugosc_nazwy = 30 ) {
        string tmp( glebokosc, '\t' ); // (16)
        os << tmp;
        if( nazwa_i_adres )
             os << setw( dlugosc_nazwy ) << nazwa << '\t' << adres << '\n';
        else
             os << adres << '\n';
       
        for( ptr_set < Element >::iterator it = elementy.begin(); it != elementy.end(); ++it )
             it->drukuj( filtr, glebokosc + 1, os, nazwa_i_adres, dlugosc_nazwy );
       
        return os;
    }
   
    static bool jestKatalogiek( dirent * plik ) { // (17)
        return( plik->d_type ) == DT_DIR; // || (plik->d_type)==DT_LNK;
    }
   
    virtual unsigned long long rozmiar( int( * filtr )( const Element & ) = NULL ) {
        unsigned long long roz = nazwa.size();
        for( ptr_set < Element >::iterator it = elementy.begin(); it != elementy.end(); ++it )
             roz += it->rozmiar( filtr );
       
        return roz;
    }
   
    virtual unsigned zmienNazwyZawierajace( string szukany, string docelowy = "", int( * filtr )( const Element & ) = NULL ) {
        unsigned zmienione = 0;
        for( ptr_set < Element >::iterator it = elementy.begin(); it != elementy.end(); ++it )
             zmienione += it->zmienNazwyZawierajace( szukany, docelowy, filtr );
       
        return zmienione;
    }
   
    virtual long unsigned drzewoKatalogowDoPliku( ostream & strumien, int( * filtr )( const Element & ) = NULL, string odstep = "\n", bool chce_nazwe_adres = true ) {
        unsigned ilosc_plikow = 0;
        for( ptr_set < Element >::iterator it = elementy.begin(); it != elementy.end(); ++it )
             ilosc_plikow += it->drzewoKatalogowDoPliku( strumien, filtr, odstep, chce_nazwe_adres );
       
        return ilosc_plikow;
    }
};

#endif
Obiecany opis niektórych zwrotów w powyższym kodzie:
  • 1
    const char * typPliku( unsigned typ ) {
    funkcja ta zwraca tekstowo typ plików. Podkreślam również że dla innych systemów operacyjnych niż Linux mogą być inne typy plików (a na pewno dowiązań symbolicznych w Windowskie nie ma) i inne makra o innych nazwach
  • 2
    trim_left_if( adres, is_any_of( "." ) );
    wyrażenie to przycina wszystkie kropki z lewej strony wyrażenia. Pytanie -skąd się biorą te kropki - jak chcemy odczytać aktualną ścieżkę, w której leży nasz program, podajemy string składający się z kropki, natomiast ścieżka do wewnętrznych plików, w sąsiedztwie naszego programu, nie zaczyna się .{nazwa} tylko po prostu nazwa
  • 3
    C/C++
    virtual ostream & drukuj( int( * filtr )( const Element & ) = NULL, unsigned glebokosc = 0, ostream & os = cout, bool nazwa_i_adres = true, unsigned dlugosc_nazwy = 30 ) {
        if( filtr == NULL )
    funkcja zajmująca się drukowaniem nazw plików i katalogów, przyjmuje za argument wskaźnik do funkcji filtrującej, gdy jest on równy NULL wtedy żaden filtr nie zostaje zastosowany. Głębokość jest używana po to aby bardziej wewnętrzne pliki miały większe wcięcia niż zewnętrzne. Kolejnym argumentem funkcji jest strumień wyjściowy, domyślnie cout. Mamy również możliwość ustawienia czy chcemy zobaczyć jedynie adresy plików, czy również nazwę odwzorowaniem takiej decyzji jest argument nazwa_i_adres. Ostatni z parametrów precyzuje ile miejsca przewidujemy na nazwę elementu w katalogu
  • 4
    virtual list < tuple < string, string, string > > plikiZawierajaceWTresci( string tresc ) = 0;
    wiem że to wygląda przerażająco i za to przepraszam, wolałem wykorzystać listę tupli niż pisać własną strukturę magazynującą, mimo złego wyglądu bardzo wygodnie mi się operuje na takiej konstrukcji wyświetlając listę plików zawierających w treści podany fragment
  • 5
    virtual unsigned usunZawierajaceWNazwie( string fragment_nazwy ) {
    jest to funkcja, mająca za zadanie usunięcie wszystkich plików (ale nie katalogów) zawierających w nazwie określony, niepusty ciąg znaków
  • 6
    C/C++
    virtual list < tuple < string, string, string > > plikiZawierajaceWTresci( string tresc ) { // (6)
        list < tuple < string, string, string > > out;
        if( typ == DT_REG ) {
            string tmp;
            trim_left_if( adres, is_any_of( "/" ) );
           
            ifstream plik( adres.c_str() );
    odpowiednik tej funkcji co poprzednio, lecz tym razem jest to implementacja z podklasy. Działanie funkcji -dla plików regularnych zczytuję linijkę za linijką i przeszukuję każdą z nich pod kątem szukanej frazy, po znalezieniu zapisuję: nazwę i adres pliku, oraz linijkę w której znalazłem tekst do "trój-stringo-tup-listy", wadą takiego rozwiązania jest to że fraza wielo-linijkowa nie zostanie znaleziona
  • 7
    C/C++
    while( !plik.eof() ) {
        getline( plik, tmp );
        if( tmp.find( tresc ) != string::npos ) { // (7)
            out.push_back( make_tuple( nazwa, adres, tmp ) );
            break;
        }
    }
    szukam plików które zawierają w treści daną zlepkę wyrazów, w związku z tym zaraz po znalezieniu kończę przeszukiwanie całego pliku
  • 8
    C/C++
    virtual unsigned long long rozmiar( int( * filtr )( const Element & ) = NULL ) { // (8)
        unsigned long long roz = nazwa.size();
        if( typ == DT_REG ) {
            if( filtr == NULL ) {
                ifstream p( adres.c_str() );
                if( p.bad() )
                     cout << "!!! Nie mozna otworzyc pliku\n";
                else
                     roz +=( p.seekg( 0, ios::end ).tellg() );
               
            }
    funkcja nazywająca się rozmiar() i z założenia mająca za zadanie oszacować pojemność wszystkich plików typu DT_REG, które spełniają kryteria ewentualnego filtra, w praktyce funkcja po otwarciu pliku pozycjonuje wskaźnik do czytania na końcu danego pliku i zwraca pozycję, wynikiem tego powinna być ilość bajtów pliku kodowanego w ASCII, na Linuxie zwrócony przez tę funkcję rozmiar nie bardzo odstaje od tego zwróconego przez system
  • 9
    virtual unsigned zmienNazwyZawierajace( string szukany, string docelowy = "", int( * filtr )( const Element & ) = NULL ) {
    zadaniem tej funkcji jest zmiana nazwy pliku (ale nie katalogu), jeżeli zawiera ona podany ciąg znaków, jest również możliwe dodanie filtra jakie pliki się chce zmieniać
  • 10
    C/C++
    virtual long unsigned drzewoKatalogowDoPliku( ostream & strumien, int( * filtr )( const Element & ) = NULL, string odstep = "\n", bool chce_nazwe_adres = true ) {
        if(( filtr == NULL ) ? true
            : filtr( * this ) )
        { //
            ifstream plik( adres.c_str() );
            if( plik.bad() )
                 cout << "!!! Nie mozna otworzyc pliku\n";
            else {
                string tmp;
                if( chce_nazwe_adres )
                     strumien << '\t' << nazwa << '\t' << adres << ":\n";
               
                while( !plik.eof() ) { //  (10)
                    getline( plik, tmp );
                    strumien << tmp << '\n';
                }
                strumien << odstep;
                return 1;
            }
        }
    funkcja wg mnie przydatna jeżeli chcemy wiele plików automatycznie wrzucić do jednego, przyjmuje ona za argument strumień wyjściowy, więc umożliwia nawet wyświetlenie dużej liczby plików na ekranie. Na uwagę zasługuje "if w if'ie", który robiłem wcześniej oddzielnie i duplikowałem ten sam kod, teraz wykorzystując operator ?: sprawdzam czy podany filtr jest NULL, jeżeli tak -warunek jest prawdziwy i bierzemy się za plik, w przeciwnym wypadku o tym czy weźmiemy się za przepisywanie pliku zadecyduje warunek filtra
  • 11
    out.insert( out.end(), tmp.begin(), tmp.end() );
    każdy plik i katalog zawiera swoją własną listę tupli, musi zostać zwrócona jedna, więc wykorzystuję funkcję listy insert aby na końcu kontenera dorzuciła wszystkie elementy ze zwróconej listy tupli, przy pustej liście też nie ma problemu, oto jest przykład wygody pracy z listą tupli
  • 12
    static void wyswietlTuplePlikow( list < tuple < string, string, string >> pliki, ostream & os = cout, string szukana = "" )
    tupla (używam wersji z biblioteki boost) została zwrócona, trzeba by ją przetrząsnąć żeby w sensowny sposób wyświetlić to co ona kryje, do tego właśnie służy ta funkcja
  • 13
    trim_left_if( tmp_adres, is_any_of( "/" ) );
    funkcja reorganizujSciezke() jest wykorzystywana nie tylko przy konstrukcji katalogów, ale też po modyfikacji drzewa katalogowego, takiej jak usuwanie plików, trim_left_if usuwa wszystkie "dzielenia" z lewej strony, które się tam znalazły podczas konstrukcji obiektów
  • 14
    closedir( sciezka );
    z początku planowałem zamknąć ścieżkę w destruktorze, ale przy otwarciu większej liczby katalogów liczba otwartych ścieżek może przekroczyć OPEN_MAX, a tym samym nie otrzymamy pełnego drzewa katalogów, dlatego zamykam po każdym sczytaniu
  • 15
    virtual unsigned usunZawierajaceWNazwie( string nazwa ) {
    ta funkcja (w strukturze Katalog) wywołuje dla każdego leżącego w nim pliku odpowiednią dla nich wersję (z wykorzystaniem polimorfizmu. Ponadto polimorficzne użycie tego typu przypomina wzorzec projektowy Interpreter)
  • 16
    string tmp( glebokosc, '\t' );
    tutaj jest konstruowany string zawierający znak '\t' tyle razy jak wielka jest glebokosc, takie coś umożliwia mi tworzenie odpowiednich wcięć dla coraz to głębszej struktury katalogowej
  • 17
    C/C++
    static bool jestKatalogiek( dirent * plik ) { // (15)
        return( plik->d_type ) == DT_DIR; // || (plik->d_type)==DT_LNK;
    }
    funkcja sprawdzająca czy dana struktura pliku jest katalogiem, nie mam pewności czy na innych niż Linux systemach będzie działała (i w ogóle się kompilowała), dlatego przy braku dobrej alternatywy można by sprawdzać czy coś jest katalogiem wywołując na tym funkcję opendir(), następnie sprawdzając wynik, jeśli wartość zwracana inna niż NULL tzn. że jest katalogiem. Wykorzystując sprawdzanie z otwieraniem ścieżki trzeba ją zamknąć po sukcesywnym otwarciu

Powyżej wstawiłem bibliotekę dającą pewne funkcje, ale pokażę również jak ze wszystkiego korzystać, pokazując kody, oraz wydruki programów:

Wyświetlanie drzewa katalogów przy pomocy powyższego kodu

C/C++
#include "plikowanie.hpp"

int mojFiltr( const Element & e ) {
    if( e.nazwa.find( ".cc" ) != string::npos )
         return 1;
    else if( e.nazwa.find( ".h" ) != string::npos )
         return 1;
   
    return 0;
}

main() {
    Katalog k( "." );
    k.drukuj();
    cout << "----------------druk po zastosowaniu filtra .cc i .h; bez podawania nazw plikow\n";
    k.drukuj( mojFiltr, 0, cout, false );
} //    g++     listowanie6wglab.cc -o listowanie6wglab && ./listowanie6wglab
Wydruk:

        PulpitWindows                   /PulpitWindows
        listowanie.c                    /listowanie.c
        listowanie2.c                   /listowanie2.c
        listowanie3.cc                  /listowanie3.cc
        listowanie4.c                   /listowanie4.c
        listowanie5                     /listowanie5
        listowanie5.c                   /listowanie5.c
        listowanie6wglab                /listowanie6wglab
        listowanie6wglab.cc             /listowanie6wglab.cc
        listowanie6wglab.cc~            /listowanie6wglab.cc~
        listowanie7.cc                  /listowanie7.cc
        plikowanie.hpp                  /plikowanie.hpp
        program_erep                    /program_erep
                Countries_01September_12:30:07.html     /program_erep/Countries_01September_12:30:07.html
                cheapest.txt                    /program_erep/cheapest.txt
                erepy.cc                        /program_erep/erepy.cc
                erepy.exe                       /program_erep/erepy.exe
                rapidxml                        /program_erep/rapidxml
                        license.txt                     /program_erep/rapidxml/license.txt
                        manual.html                     /program_erep/rapidxml/manual.html
                        rapidxml.hpp                    /program_erep/rapidxml/rapidxml.hpp
                        rapidxml_iterators.hpp          /program_erep/rapidxml/rapidxml_iterators.hpp
                        rapidxml_print.hpp              /program_erep/rapidxml/rapidxml_print.hpp
                        rapidxml_utils.hpp              /program_erep/rapidxml/rapidxml_utils.hpp
        suma.txt                        /suma.txt
        tmp.c                           /tmp.c
        tmp.cc                          /tmp.cc
----------------druk po zastosowaniu filtra .cc i .h; bez podawania nazw plikow

        /listowanie3.cc
        /listowanie6wglab.cc
        /listowanie6wglab.cc~
        /listowanie7.cc
        /plikowanie.hpp
        /program_erep
                /program_erep/Countries_01September_12:30:07.html
                /program_erep/erepy.cc
                /program_erep/rapidxml
                        /program_erep/rapidxml/manual.html
                        /program_erep/rapidxml/rapidxml.hpp
                        /program_erep/rapidxml/rapidxml_iterators.hpp
                        /program_erep/rapidxml/rapidxml_print.hpp
                        /program_erep/rapidxml/rapidxml_utils.hpp
        /tmp.cc

Liczenie elementów i rozmiaru

C/C++
#include "plikowanie.hpp"

int mojFiltr( const Element & e ) {
    if( e.nazwa.find( ".cc" ) != string::npos )
         return 1;
    else if( e.nazwa.find( ".h" ) != string::npos )
         return 1;
   
    return 0;
}

main() {
    Katalog k( "." );
    cout << "KATALOG maja elementow: " << k.przeliczElementy() << " \n";
    cout << "rozmiar katalogu w b liczac tylko pliku .cc i .h = " << k.rozmiar( mojFiltr ) << endl;
} //    g++     listowanie7.cc -o listowanie7 && ./listowanie7
Wydruk, proszę mieć na uwadze że przy każdym następnym kodzie w drzewie katalogów jest o jeden plik więcej :)

KATALOG maja elementow: 28
rozmiar katalogu w b liczac tylko pliku .cc i .h = 392

Wyświetlanie plików zawierających w treści szukany fragment tekstu

C/C++
#include "plikowanie.hpp"

main() {
    Katalog k( "." );
   
    string szukana( "gcc " );
    Katalog::wyswietlTuplePlikow( k.plikiZawierajaceWTresci( szukana ), cout, szukana );
} //    g++     listowanie8.cc -o listowanie8 && ./listowanie8
Wydruk, w którym widać, że pliki binarne również są przeszukiwane, przez co widać "krzaczki":

W sumie znaleziono 10 plikow zawierajacych szukany tekst gcc

1       PLIK: 'listowanie.c' LEZACY POD ADRESEM: 'listowanie.c' MIESCI FRAZE W LINIJCE:
}       //    gcc     listowanie.c -o listowanie && ./listowanie

2       PLIK: 'listowanie2.c' LEZACY POD ADRESEM: 'listowanie2.c' MIESCI FRAZE W LINIJCE:
}       //    gcc     listowanie2.c -o listowanie2 && ./listowanie2

3       PLIK: 'listowanie4.c' LEZACY POD ADRESEM: 'listowanie4.c' MIESCI FRAZE W LINIJCE:
}       //    gcc     listowanie4.c -o listowanie4 && ./listowanie4

4       PLIK: 'listowanie5.c' LEZACY POD ADRESEM: 'listowanie5.c' MIESCI FRAZE W LINIJCE:
}       //    gcc     listowanie5.c -o listowanie5 && ./listowanie5

5       PLIK: 'listowanie8' LEZACY POD ADRESEM: 'listowanie8' MIESCI FRAZE W LINIJCE:
..gcc Null pointer in 'ptr_set::insert()'list::_M_check_equal_allocators/usr/include/boost/range/iterator_range.hpp!is_singular()/usr/include/boost/ptr_container/indirect_fun.hppl != 0 && r != 0IteratorT boost::iterator_range<IteratorT>::end() const [with IteratorT = const char*]IteratorT boost::iterator_range<IteratorT>::begin() const [with IteratorT = const char*]typename boost::result_of<Fun(Arg1&, Arg2&)>::type boost::void_ptr_indirect_fun<Fun, Arg1, Arg2>::operator()(const void*, const void*) const [with Fun = std::less<Element>, Arg1 = Element, Arg2 = Element, typename boost::result_of<Fun(Arg1&, Arg2&)>::type = bool]IteratorT boost::iterator_range<IteratorT>::end() const [with IteratorT = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >]IteratorT boost::iterator_range<IteratorT>::begin() const [with IteratorT = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >]bool boost::iterator_range<IteratorT>::empty() const [with IteratorT = __gnu_cxx::__normal_iterator<char*, std::basic_string<char> >]IteratorT boost::iterator_range<IteratorT>::end() const [with IteratorT = __gnu_cxx::__normal_iterator<char*, std::basic_string<char> >]IteratorT boost::iterator_range<IteratorT>::begin() const [with IteratorT = __gnu_cxx::__normal_iterator<char*, std::basic_string<char> >7Element�qt~�(\7Katalogh�
        N5boost11bad_pointerEhN5boost27bad_ptr_container_operationEhP
6       PLIK: 'listowanie8 (kopia).cc' LEZACY POD ADRESEM: 'listowanie8 (kopia).cc' MIESCI FRAZE W LINIJCE:
  string szukana("gcc ");

7       PLIK: 'listowanie8.cc' LEZACY POD ADRESEM: 'listowanie8.cc' MIESCI FRAZE W LINIJCE:
  string szukana("gcc ");

8       PLIK: 'listowanie8.cc~' LEZACY POD ADRESEM: 'listowanie8.cc~' MIESCI FRAZE W LINIJCE:
  string szukana("gcc ");

9       PLIK: 'manual.html' LEZACY POD ADRESEM: 'program_erep/rapidxml/manual.html' MIESCI FRAZE W LINIJCE:
 Entire parser is contained in a single header file, so no building or linking is neccesary. To use it you just need to copy <code>rapidxml.hpp</code> file to a convenient place (such as your project directory), and include it where needed. You may also want to use printing functions contained in header <code>rapidxml_print.hpp</code>.</para><sect2><h3 id="namespacerapidxml_1dependencies_and_compatibility">1.1 Dependencies And Compatibility</h3><para>RapidXml has <i>no dependencies</i> other than a very small subset of standard C++ library (<code>&lt;cassert&gt;</code>, <code>&lt;cstdlib&gt;</code>, <code>&lt;new&gt;</code> and <code>&lt;exception&gt;</code>, unless exceptions are disabled). It should compile on any reasonably conformant compiler, and was tested on Visual C++ 2003, Visual C++ 2005, Visual C++ 2008, gcc 3, gcc 4, and Comeau 4.3.3. Care was taken that no warnings are produced on these compilers, even with highest warning levels enabled.</para></sect2><sect2><h3 id="namespacerapidxml_1character_types_and_encodings">1.2 Character Types And Encodings</h3><para>RapidXml is character type agnostic, and can work both with narrow and wide characters. Current version does not fully support UTF-16 or UTF-32, so use of wide characters is somewhat incapacitated. However, it should succesfully parse <code>wchar_t</code> strings containing UTF-16 or UTF-32 if endianness of the data matches that of the machine. UTF-8 is fully supported, including all numeric character references, which are expanded into appropriate UTF-8 byte sequences (unless you enable parse_no_utf8 flag). <br/><br/>

10      PLIK: 'tmp.c' LEZACY POD ADRESEM: 'tmp.c' MIESCI FRAZE W LINIJCE:
}       /*    gcc     tmp.c -g  -o tmp && ./tmp */

Usuwanie wszystkich plików (z możliwym filtrem), które zawierają w nazwie napis

C/C++
#include "plikowanie.hpp"

main() {
    Katalog k( "." );
    cout << "KATALOG NA STARCIE majacy elementow: " << k.przeliczElementy() << " \n";
    k.drukuj();
    cout << "usunieto plikow " << k.usunZawierajaceWNazwie( "~" ) << " bo zawieraly w nazwie ~" << endl;
    cout << "KATALOG PO USUNIECIU majacy elementow: " << k.przeliczElementy() << " \n";
    k.drukuj();
} //    g++     listowanie9.cc -o listowanie9 && ./listowanie9
Wydruk:

KATALOG NA STARCIE majacy elementow: 32
                             
        PulpitWindows                   /PulpitWindows
        listowanie.c                    /listowanie.c
        listowanie2.c                   /listowanie2.c
        listowanie3.cc                  /listowanie3.cc
        listowanie4.c                   /listowanie4.c
        listowanie5.c                   /listowanie5.c
        listowanie6wglab.cc             /listowanie6wglab.cc
        listowanie7.cc                  /listowanie7.cc
        listowanie7.cc~                 /listowanie7.cc~
        listowanie8                     /listowanie8
        listowanie8.cc                  /listowanie8.cc
        listowanie9                     /listowanie9
        listowanie9 (kopia).cc          /listowanie9 (kopia).cc
        listowanie9.cc                  /listowanie9.cc
        listowanie9.cc~                 /listowanie9.cc~
        plikowanie.hpp                  /plikowanie.hpp
        program_erep                    /program_erep
                Countries_01September_12:30:07.html     /program_erep/Countries_01September_12:30:07.html
                cheapest.txt                    /program_erep/cheapest.txt
                cheapest.txt~                   /program_erep/cheapest.txt~
                erepy.cc                        /program_erep/erepy.cc
                erepy.exe                       /program_erep/erepy.exe
                rapidxml                        /program_erep/rapidxml
                        license.txt                     /program_erep/rapidxml/license.txt
                        license.txt~                    /program_erep/rapidxml/license.txt~
                        manual.html                     /program_erep/rapidxml/manual.html
                        manual.html~                    /program_erep/rapidxml/manual.html~
                        rapidxml.hpp                    /program_erep/rapidxml/rapidxml.hpp
                        rapidxml_iterators.hpp          /program_erep/rapidxml/rapidxml_iterators.hpp
                        rapidxml_print.hpp              /program_erep/rapidxml/rapidxml_print.hpp
                        rapidxml_utils.hpp              /program_erep/rapidxml/rapidxml_utils.hpp
        tmp.c                           /tmp.c
        tmp.cc                          /tmp.cc
        tmp.c~                          /tmp.c~
usunieto plikow 6 bo zawieraly w nazwie ~
KATALOG PO USUNIECIU majacy elementow: 26
                             
        PulpitWindows                   /PulpitWindows
        listowanie.c                    /listowanie.c
        listowanie2.c                   /listowanie2.c
        listowanie3.cc                  /listowanie3.cc
        listowanie4.c                   /listowanie4.c
        listowanie5.c                   /listowanie5.c
        listowanie6wglab.cc             /listowanie6wglab.cc
        listowanie7.cc                  /listowanie7.cc
        listowanie8                     /listowanie8
        listowanie8.cc                  /listowanie8.cc
        listowanie9                     /listowanie9
        listowanie9 (kopia).cc          /listowanie9 (kopia).cc
        listowanie9.cc                  /listowanie9.cc
        plikowanie.hpp                  /plikowanie.hpp
        program_erep                    /program_erep
                Countries_01September_12:30:07.html     /program_erep/Countries_01September_12:30:07.html
                cheapest.txt                    /program_erep/cheapest.txt
                erepy.cc                        /program_erep/erepy.cc
                erepy.exe                       /program_erep/erepy.exe
                rapidxml                        /program_erep/rapidxml
                        license.txt                     /program_erep/rapidxml/license.txt
                        manual.html                     /program_erep/rapidxml/manual.html
                        rapidxml.hpp                    /program_erep/rapidxml/rapidxml.hpp
                        rapidxml_iterators.hpp          /program_erep/rapidxml/rapidxml_iterators.hpp
                        rapidxml_print.hpp              /program_erep/rapidxml/rapidxml_print.hpp
                        rapidxml_utils.hpp              /program_erep/rapidxml/rapidxml_utils.hpp
        tmp.c                           /tmp.c
        tmp.cc                          /tmp.cc

Zmiana nazw wielu plików, zawierających pewien fragment w nazwie

C/C++
#include "plikowanie.hpp"

main() {
    Katalog k( "." );
    k.drukuj();
   
    string nazwy_zawierajace( "uwielbiamJAVE" );
    cout << "zmieniono nazwy zawierajace '" << nazwy_zawierajace << "' w ilosci = " << k.zmienNazwyZawierajace( nazwy_zawierajace, "uwielbiamC++" ) << endl;
    k.drukuj();
} //    g++     listowanie10.cc -o listowanie10 && ./listowanie10
Wydruk powyższego programu:
                        
        PulpitWindows                   /PulpitWindows
        listowanie.c                    /listowanie.c
        listowanie10                    /listowanie10
        listowanie10.cc                 /listowanie10.cc
        listowanie10.cc~                /listowanie10.cc~
        listowanie11.cc                 /listowanie11.cc
        listowanie11.cc~                /listowanie11.cc~
        listowanie2.c                   /listowanie2.c
        listowanie3.cc                  /listowanie3.cc
        listowanie4.c                   /listowanie4.c
        listowanie5.c                   /listowanie5.c
        listowanie6wglab.cc             /listowanie6wglab.cc
        listowanie7.cc                  /listowanie7.cc
        listowanie8.cc                  /listowanie8.cc
        listowanie9.cc                  /listowanie9.cc
        plikowanie.hpp                  /plikowanie.hpp
        program_erep                    /program_erep
                Countries_01September_12:30:07.html     /program_erep/Countries_01September_12:30:07.html
                cheapest.txt                    /program_erep/cheapest.txt
                erepy.cc                        /program_erep/erepy.cc
                erepy.exe                       /program_erep/erepy.exe
                nie_lubie_infy_ale_uwielbiamJAVE.sdf    /program_erep/nie_lubie_infy_ale_uwielbiamJAVE.sdf
                rapidxml                        /program_erep/rapidxml
                        ale._uwielbiamJAVE_zeHEJ.pl     /program_erep/rapidxml/ale._uwielbiamJAVE_zeHEJ.pl
                        license.txt                     /program_erep/rapidxml/license.txt
                        manual.html                     /program_erep/rapidxml/manual.html
                        rapidxml.hpp                    /program_erep/rapidxml/rapidxml.hpp
                        rapidxml_iterators.hpp          /program_erep/rapidxml/rapidxml_iterators.hpp
                        rapidxml_print.hpp              /program_erep/rapidxml/rapidxml_print.hpp
                        rapidxml_utils.hpp              /program_erep/rapidxml/rapidxml_utils.hpp
        tmp.c                           /tmp.c
        tmp.cc                          /tmp.cc
        uwielbiamJAVE_ale_pale.sdf      /uwielbiamJAVE_ale_pale.sdf
zmieniono nazwy zawierajace 'uwielbiamJAVE' w ilosci = 3
 
        PulpitWindows                   /PulpitWindows
        listowanie.c                    /listowanie.c
        listowanie10                    /listowanie10
        listowanie10.cc                 /listowanie10.cc
        listowanie10.cc~                /listowanie10.cc~
        listowanie11.cc                 /listowanie11.cc
        listowanie11.cc~                /listowanie11.cc~
        listowanie2.c                   /listowanie2.c
        listowanie3.cc                  /listowanie3.cc
        listowanie4.c                   /listowanie4.c
        listowanie5.c                   /listowanie5.c
        listowanie6wglab.cc             /listowanie6wglab.cc
        listowanie7.cc                  /listowanie7.cc
        listowanie8.cc                  /listowanie8.cc
        listowanie9.cc                  /listowanie9.cc
        plikowanie.hpp                  /plikowanie.hpp
        program_erep                    /program_erep
                Countries_01September_12:30:07.html     /program_erep/Countries_01September_12:30:07.html
                cheapest.txt                    /program_erep/cheapest.txt
                erepy.cc                        /program_erep/erepy.cc
                erepy.exe                       /program_erep/erepy.exe
                nie_lubie_infy_ale_uwielbiamC++.sdf    program_erep/nie_lubie_infy_ale_uwielbiamC++.sdf
                rapidxml                        /program_erep/rapidxml
                        ale._uwielbiamC++_zeHEJ.pl     program_erep/rapidxml/ale._uwielbiamC++_zeHEJ.pl
                        license.txt                     /program_erep/rapidxml/license.txt
                        manual.html                     /program_erep/rapidxml/manual.html
                        rapidxml.hpp                    /program_erep/rapidxml/rapidxml.hpp
                        rapidxml_iterators.hpp          /program_erep/rapidxml/rapidxml_iterators.hpp
                        rapidxml_print.hpp              /program_erep/rapidxml/rapidxml_print.hpp
                        rapidxml_utils.hpp              /program_erep/rapidxml/rapidxml_utils.hpp
        tmp.c                           /tmp.c
        tmp.cc                          /tmp.cc
        uwielbiamC++_ale_pale.sdf      uwielbiamC++_ale_pale.sdf

Zapisywanie plików tekstowych, spełniających kryteria na wybrany strumień -np. do pliku

C/C++
#include "plikowanie.hpp"

int mojFiltr( const Element & e ) {
    if( e.nazwa.find( ".cc" ) != string::npos )
         return 1;
   
    return 0;
}

main() {
    Katalog k( "." );
   
    ofstream plik_sumacyjny( "suma.txt" );
    cout << "zapisano wszystkie pliki .cc do pliku 'suma.cc' w ilości plików:\t" << k.drzewoKatalogowDoPliku( plik_sumacyjny, mojFiltr, "\n___\n\n" ) << endl;
} //    g++     listowanie11.cc -o listowanie11 && ./listowanie11
W przypadku tego programu nie będę umieszczał wydruków ani do pliku ani na ekran ze względów objętościowych, napomnę jedynie o jednej ważnej informacji - jeśli spisujemy wiele plików do pewnego pliku, trzeba zadbać aby go jeszcze nie było w momencie wywoływania tego polecenia lub aby leżał poza ścieżka przeszukiwań, w przeciwnym razie może się pojawić nieskończone czytanie i równoczesne zapisywanie do tego pliku.

Wyszukiwanie "plagiatów"

Na zakończenie artykułu o tej bibliotece dorzucam prosty kod na wyszukiwanie plików z danego katalogu (ale nie danej listy katalogów) mających parę następujących po sobie wyrazów takich samych. Ogólna zasada jest taka że dzielę plik na wyrazy wg znaków przystankowych " ! ?.,;()[]{}'\":\n", wrzucam do listy. Mając 2 listy przeszukuję je dla każdej kombinacji wyrazów (zachowując kolejność) w poszukiwaniu takich samych wyrazów, po znalezieniu identycznego słowa przeszukuję następne w obydwu listach w celu znalezienia najdłuższego, pasującego ciągu. Minusem tego rozwiązania jest to, że ciąg zawierający n wyrazów będzie wyświetlony n razy, mógłbym temu zaradzić, ale zdarza się że dany ciąg powtarza się więcej niż raz w różnych miejscach, dlatego nie chciałem go eliminować po pierwszym odnalezieniu, bo gdyby przypadkiem pojawiał się dłuższy później -zostałby ominięty; dla osób nie lubiących takiego rozwiązania dałem możliwość sprecyzowania minimalną ilość wyrazów do wyświetlenia. Jeszcze jedna uwaga - program sobie nie radzi z plikami, które zawierają zwykłe " " (spacje) w nazwie.
Ostatnia dygresja - wiele z tego programu jest napisane z użyciem funkcjonalności z C (np. FILE, unikałem używania std::string, ciężkich chociaż lubianych kontenerów, itp...), zrobiłem to gdyż zależy mi na wydajności gdy ktoś będzie chciał przeszukiwać naprawdę wiele plików, jako rekompensatę używam paru funkcjonalności i ulepszeń z C++0x, dlatego trzeba program odpowiednio kompilować.
Oto kod programu na wyszukiwanie plagiatów:
C/C++
#include <iostream>
#include <fstream>
#include <dirent.h>
#include <cstdio>
#include <cerrno>
#include <string>
#include <cstring>
#include <set>
#include <iomanip>
#include <map>
#include <tuple>
#include <forward_list>
#include <boost/algorithm/string.hpp>
using namespace std;
using namespace boost;

int pokazErrno();

forward_list < string > plikNaWyrazy( const char * nazwa_pliku ) {
    forward_list < string > wyrazy;
   
    const char * dstepy = " ! ?.,;()[]{}'\":\n"; // (1)
    char znak[ 2 ] = { '\0', '\0' }; //
    FILE * plik = fopen( nazwa_pliku, "r" );
    string bufor_wyrazowy;
   
    while(( znak[ 0 ] = fgetc( plik ) ) != EOF ) {
        if( strrchr( dstepy, znak[ 0 ] ) == NULL )
             bufor_wyrazowy.append( znak ); // (2)
        else {
            trim( bufor_wyrazowy ); //
            to_lower( bufor_wyrazowy ); //
           
            if( !bufor_wyrazowy.empty() ) //
                 wyrazy.push_front( bufor_wyrazowy );
           
            bufor_wyrazowy.clear();
        }
    }
   
    fclose( plik );
    return wyrazy;
}

unsigned wyswietlWyrazy( forward_list < string >& wyrazy, ostream & strumien = cout, const char * odstep = "\t" ) { // (3)
    unsigned i = 0;
    wyrazy.reverse();
    for( auto it = wyrazy.cbegin(); it != wyrazy.cend(); ++it ) {
        strumien << * it << odstep;
        ++i;
    }
    wyrazy.reverse();
    return i;
}

multimap < unsigned, string > szukajPodobienstw( forward_list < string >& wyrazy1, forward_list < string >& wyrazy2 ) { // (4)
    multimap < unsigned, string > ciagi_podobne;
    string tmp;
   
    for( forward_list < string >::iterator it1 = wyrazy1.begin(), pomocniczy1; it1 != wyrazy1.end(); ++it1 ) {
        for( forward_list < string >::iterator it2 = wyrazy2.begin(), pomocniczy2; it2 != wyrazy2.end(); ++it2 ) {
            if(( * it1 ) ==( * it2 ) ) {
                unsigned wyrazy_podobne = 0;
                for( pomocniczy1 = it1, pomocniczy2 = it2; pomocniczy1 != wyrazy1.cend(), pomocniczy2 != wyrazy2.cend(); ++pomocniczy1, ++pomocniczy2 ) {
                    if(( * pomocniczy1 ) ==( * pomocniczy2 ) ) {
                        ++wyrazy_podobne;
                        tmp =( * pomocniczy2 ) + ' ' + tmp;
                    }
                    else {
                        ciagi_podobne.insert( make_pair( wyrazy_podobne, tmp ) );
                        tmp.clear();
                        break;
                    }
                }
                if( !tmp.empty() ) {
                    ciagi_podobne.insert( make_pair( wyrazy_podobne, tmp ) );
                    tmp.clear();
                }
            }
        }
    }
    return ciagi_podobne;
}

unsigned long pokazPodobienstwa( multimap < unsigned, string > & wyrazy, unsigned ilo_wyrazowe_podobienstwa = 3, ostream & strumien = cout ) { // (5)
    unsigned long punkty_podobienstwa = 0;
    for( auto it = wyrazy.cbegin(); it != wyrazy.cend(); ++it )
    if( it->first >= ilo_wyrazowe_podobienstwa ) {
        strumien << it->first << " wyrazowe podobienstwo w wyrazach '" << it->second << "' punktowane: " << it->first * it->first << '\n';
        punkty_podobienstwa += it->first * it->first;
    }
    return punkty_podobienstwa;
}

set < string > listujPlikiPosortowane( const char * nazwa_sciezki, int( * filtr )( dirent * ) ) { // (6a)
    set < string > nazwy_plikow;
   
    struct dirent * plik;
    DIR * sciezka;
   
    if(( sciezka = opendir( nazwa_sciezki ) ) ) {
        pokazErrno();
       
        while(( plik = readdir( sciezka ) ) )
        if( plik->d_type == DT_REG )
        if( filtr( plik ) )
             nazwy_plikow.insert( plik->d_name );
       
        pokazErrno();
       
        closedir( sciezka );
        pokazErrno();
    }
    pokazErrno();
   
    return nazwy_plikow;
}

set < string > listujPlikiPosortowane( const char * nazwa_sciezki ) { // (6b)
    set < string > nazwy_plikow;
   
    struct dirent * plik;
    DIR * sciezka;
   
    if(( sciezka = opendir( nazwa_sciezki ) ) ) {
        pokazErrno();
       
        while(( plik = readdir( sciezka ) ) )
        if( plik->d_type == DT_REG )
             nazwy_plikow.insert( plik->d_name );
       
        pokazErrno();
       
        closedir( sciezka );
        pokazErrno();
    }
    pokazErrno();
   
    return nazwy_plikow;
}


int pokazErrno() {
    int errno_kopia = errno;
    if( errno ) {
        printf( "errno = %d:\t", errno ); fflush( stdout );
        switch( errno ) {
        case EACCES: perror( "! (problem w OPENDIR) brak dostepu do czytanej sciezki" ); break;
        case ELOOP: perror( "! (problem w OPENDIR) zbyt wiele symbolicznych powiązań napotkanych w ścieżce" ); break;
        case ENAMETOOLONG: perror( "! (problem w OPENDIR) długość ścieżki przekracza PATH_MAX, lub długość nazw jej komponentów przekracza NAME_MAX" ); break;
        case ENOENT: perror( "! (problem w OPENDIR) ścieżka jest pustym stringiem, lub (problem w OPENDIR lub READDIR) składnik ścieżki nie jest nazwą istniejącej ścieżki\n" ); break;
        case ENOTDIR: perror( "! (problem w OPENDIR) komponent lub nazwa ścieżki nie jest ścieżką" ); break;
        case EMFILE: perror( "! (problem w OPENDIR) jest otwarta liczba plików równa OPEN_MAX" ); break;
        case ENFILE: perror( "! (problem w OPENDIR) zbyt wiele plików jest obecnie otwartych przez system" ); break;
        case EOVERFLOW: perror( "! (problem w READDIR) jedna z wartości w strukturze nie może być zwrócona poprawnie" ); break;
        case EBADF: perror( "! (problem w READDIR lub CLOSEDIR) argument funkcji readdir nie odnosi się do otwartego strumienia ścieżki" ); break;
        case EINTR: perror( "! (problem w CLOSEDIR) pojawia się wtedy gry funkcja jest przerwana przez sygnał" ); break;
        };
        errno = 0;
        printf( "\n" );
    }
    return errno_kopia;
}

void szukajPlagiatow( const char * plik_zrodlowy, const char * sciezka, int( * filtr )( dirent * ) = NULL, ostream & strumien = cout, unsigned min_ilosc_punktow_za_wyrazy = 3 ) {
    forward_list < string > zrodlo = plikNaWyrazy( plik_zrodlowy );
    set < string > lista_plikow;
    unsigned max_poobnych_wyrazow = 0;
   
    forward_list < tuple < unsigned long, string, multimap < unsigned, string >>> suma_podobienstw; // (7)
   
    if( filtr == NULL ) // (8)
         lista_plikow = listujPlikiPosortowane( sciezka );
    else
         lista_plikow = listujPlikiPosortowane( sciezka, filtr );
   
    for( auto & i: lista_plikow ) { // 8b
        forward_list < string > przeszukiwany = plikNaWyrazy(( string( sciezka ) + "/" + i ).c_str() );
        multimap < unsigned int, string > podobienstwa = szukajPodobienstw( zrodlo, przeszukiwany );
        max_poobnych_wyrazow = pokazPodobienstwa( podobienstwa );
        suma_podobienstw.push_front( make_tuple( max_poobnych_wyrazow, i, podobienstwa ) );
    }
   
    if( max_poobnych_wyrazow >= pow( min_ilosc_punktow_za_wyrazy, 1./ 2.) ) // (8c)
         strumien << "Dla pliku '" << plik_zrodlowy << "' jest zawierajacy " <<( max_poobnych_wyrazow + 1 ) << " punktow za wyrazy, lista:\n";
    else
         strumien << "Dla pliku '" << plik_zrodlowy << "' nie znaleziono zawierajacego co najmniej " << min_ilosc_punktow_za_wyrazy << " punktow za wyrazy\n";
   
    for( auto & i: suma_podobienstw ) { // (9)
        if( get < 0 >( i ) > 0 ) {
            strumien << "PUNKTOW ZA WYRAZY: " << get < 0 >( i ) << " w pliku o nazwie '" << get < 1 >( i ) << "' a oto lista:\n";
            for( multimap < unsigned, string >::iterator it = get < 2 >( i ).begin(); it != get < 2 >( i ).end(); ++it )
            if(( * it ).first >= min_ilosc_punktow_za_wyrazy )
                 strumien << "\t-) " <<( * it ).first << '\t' <<( * it ).second << '\n';
           
        }
    }
}

main() {
    forward_list < string > fl = plikNaWyrazy( "test.txt" );
    ofstream plik( "output.txt" );
    wyswietlWyrazy( fl, plik, "\n" );
    plik << "_________________________\n";
    forward_list < string > fl2 = plikNaWyrazy( "test2.txt" );
    wyswietlWyrazy( fl2, plik, "\n" );
    multimap < unsigned, string > podobienstwa = szukajPodobienstw( fl, fl2 );
    plik << "\nPunkty podobienstwa:\t" << pokazPodobienstwa( podobienstwa, 1, plik ) << endl;
   
    szukajPlagiatow( "test.txt", "teksty" ); // (10)
} //    g++-4.6 -std=c++0x plagiaty.cc -o plagiaty && ./plagiaty
Wydruk programu (tylko ten na ekran):

3 wyrazowe podobienstwo w wyrazach 'ipsum is simply ' punktowane: 9
4 wyrazowe podobienstwo w wyrazach 'ipsum is simply dummy ' punktowane: 16
5 wyrazowe podobienstwo w wyrazach 'ipsum is simply dummy text ' punktowane: 25
3 wyrazowe podobienstwo w wyrazach 'of esperanto was ' punktowane: 9
3 wyrazowe podobienstwo w wyrazach 'of esperanto was ' punktowane: 9
3 wyrazowe podobienstwo w wyrazach 'to persecute esperanto ' punktowane: 9
4 wyrazowe podobienstwo w wyrazach 'to persecute esperanto because ' punktowane: 16
5 wyrazowe podobienstwo w wyrazach 'to persecute esperanto because zamenhof ' punktowane: 25
3 wyrazowe podobienstwo w wyrazach 'of esperanto was ' punktowane: 9
3 wyrazowe podobienstwo w wyrazach 'of esperanto was ' punktowane: 9
3 wyrazowe podobienstwo w wyrazach 'good morning bonan ' punktowane: 9
4 wyrazowe podobienstwo w wyrazach 'good morning bonan matenon ' punktowane: 16
3 wyrazowe podobienstwo w wyrazach 'core vocabulary of ' punktowane: 9
4 wyrazowe podobienstwo w wyrazach 'core vocabulary of esperanto ' punktowane: 16
5 wyrazowe podobienstwo w wyrazach 'core vocabulary of esperanto was ' punktowane: 25
6 wyrazowe podobienstwo w wyrazach 'core vocabulary of esperanto was defined ' punktowane: 36
7 wyrazowe podobienstwo w wyrazach 'core vocabulary of esperanto was defined by ' punktowane: 49
8 wyrazowe podobienstwo w wyrazach 'core vocabulary of esperanto was defined by lingvo ' punktowane: 64
9 wyrazowe podobienstwo w wyrazach 'core vocabulary of esperanto was defined by lingvo internacia ' punktowane: 81
10 wyrazowe podobienstwo w wyrazach 'core vocabulary of esperanto was defined by lingvo internacia published ' punktowane: 100
3 wyrazowe podobienstwo w wyrazach 'are also two ' punktowane: 9
Dla pliku 'test.txt' nie znaleziono zawierajacego co najmniej 3 punktow za wyrazy
PUNKTOW ZA WYRAZY: 9 w pliku o nazwie 'a8.txt' a oto lista:
        -) 3    are also two
PUNKTOW ZA WYRAZY: 380 w pliku o nazwie 'a4.txt' a oto lista:
        -) 3    core vocabulary of
        -) 4    core vocabulary of esperanto
        -) 5    core vocabulary of esperanto was
        -) 6    core vocabulary of esperanto was defined
        -) 7    core vocabulary of esperanto was defined by
        -) 8    core vocabulary of esperanto was defined by lingvo
        -) 9    core vocabulary of esperanto was defined by lingvo internacia
        -) 10   core vocabulary of esperanto was defined by lingvo internacia published
PUNKTOW ZA WYRAZY: 34 w pliku o nazwie 'a3.txt' a oto lista:
        -) 3    of esperanto was
        -) 3    good morning bonan
        -) 4    good morning bonan matenon
PUNKTOW ZA WYRAZY: 9 w pliku o nazwie 'a16.txt' a oto lista:
        -) 3    of esperanto was
PUNKTOW ZA WYRAZY: 68 w pliku o nazwie 'a13.txt' a oto lista:
        -) 3    of esperanto was
        -) 3    of esperanto was
        -) 3    to persecute esperanto
        -) 4    to persecute esperanto because
        -) 5    to persecute esperanto because zamenhof
PUNKTOW ZA WYRAZY: 50 w pliku o nazwie 'a.txt' a oto lista:
        -) 3    ipsum is simply
        -) 4    ipsum is simply dummy
        -) 5    ipsum is simply dummy text
(W plikach były różne fragmenty Wikipedii o esperanto, oraz tekst Lorem ipsum).

Wyjaśnienie ciekawszych/trudniejszych/bardziej zawiłych części programu:
  • 1
    C/C++
    const char * dstepy = " ! ?.,;()[]{}'\":\n";
    char znak[ 2 ] = { '\0', '\0' };
    "odstępy" są to znaki, które założyłem że dzielą wyrazy. Linijka zawierająca 2-elementową tablicę znakową wygląda dziwnie, ale dlatego że zczytuję zawsze jeden znak, a do funkcji strrchr() potrzebuję łańcucha znakowego, rzutowanie nie załatwi sprawy, gdyż potrzebny jest znak końca stringa na drugiej pozycji
  • 2
    C/C++
    while(( znak[ 0 ] = fgetc( plik ) ) != EOF ) {
        if( strrchr( dstepy, znak[ 0 ] ) == NULL )
             bufor_wyrazowy.append( znak ); // (2)
        else {
            trim( bufor_wyrazowy ); //
            to_lower( bufor_wyrazowy ); //
           
            if( !bufor_wyrazowy.empty() ) //
                 wyrazy.push_front( bufor_wyrazowy );
           
            bufor_wyrazowy.clear();
        }
    z pliku pobierany jest jeden znak, zależnie czy NIE jest jednym z "odstępów między wyrazami" czy jest, odpowiednio jest dorzucany do bufora wyrazów, w przeciwnym wypadku bufor wyrazów jest obcinany od białych znaków, zmieniany na małe litery i dodawany do listy. A propos listy -jest to typ forward_list<string> -nowy twór C++0x będący maksymalnie optymalną listą jednokierunkową, dlatego nie ma w niej operacji push_back, ponadto jak używam tylko push_front wyrazy są w odwrotnej kolejności
  • 3
    C/C++
    unsigned wyswietlWyrazy( forward_list < string >& wyrazy, ostream & strumien = cout, const char * odstep = "\t" ) { // (3)
        unsigned i = 0;
        wyrazy.reverse();
        for( auto it = wyrazy.cbegin(); it != wyrazy.cend(); ++it ) {
            strumien << * it << odstep;
            ++i;
        }
        wyrazy.reverse();
        return i;
    }
    w tej funkcji nie ma nic skomplikowanego, po prostu wyświetla wyrazy na odpowiedni strumień wyjściowy wstawiając między nie zdefiniowany odstęp. Jak pamiętamy w liście wyrazy są usytuowane w odwrotnej kolejności, w związku z tym do wyświetlania trzeba ją odwrócić, po wyświetleniu warto byłoby wykonać operację odwracania ponownie aby algorytm porównywania miał wyrazy w takiej samej kolejności
  • 4
    C/C++
    multimap < unsigned, string > szukajPodobienstw( forward_list < string >& wyrazy1, forward_list < string >& wyrazy2 ) { // (4)
        multimap < unsigned, string > ciagi_podobne;
    ta funkcja jest istotą całego wyszukiwania plagiatów. Przyjmuje 2 listy wyrazów, które porównuje "każdy z każdym", jeżeli znajdzie takie same wyrazy przejeżdża od tego miejsca do miejsca aż wyrazy będą się różnić w obydwu listach. Pasujące podciągi są dodawane do multimapy a każda para zawiera liczbę identycznych wyrazów i te wyrazy
  • 5
    unsigned long pokazPodobienstwa( multimap < unsigned, string > & wyrazy, unsigned ilo_wyrazowe_podobienstwa = 3, ostream & strumien = cout )
    funkcja wyświetla podobieństwa między listami wyrazów (a dokładniej liczbę wyrazów), te wyrazy oraz punkty będące kwadratem liczby wyrazów, suma tych punktów jest zwracana jako rezultat funkcji. Dodam że użytkownik może ustawiń ilo-wyrazowe podobieństwa go interesują
  • 6a i 6b
    C/C++
    set < string > listujPlikiPosortowane( const char * nazwa_sciezki, int( * filtr )( dirent * ) ) { // (6a)
        set < string > nazwy_plikow;
       
        struct dirent * plik;
        DIR * sciezka;
       
        if(( sciezka = opendir( nazwa_sciezki ) ) ) {
            pokazErrno();
           
            while(( plik = readdir( sciezka ) ) )
            if( plik->d_type == DT_REG )
            if( filtr( plik ) )
                 nazwy_plikow.insert( plik->d_name );
           
            pokazErrno();
           
            closedir( sciezka );
            pokazErrno();
        }
        pokazErrno();
       
        return nazwy_plikow;
    }
    funkcja podobna do tych z innych programów, ale tym zarem kwestia filtrowania lub jego braku jest rozwiązana poprzez 2 przeciążone funkcje zamiast jednej z kolejnym warunkiem if
  • 7
    forward_list < tuple < unsigned long, string, multimap < unsigned, string >>> suma_podobienstw;
    wygląda przerażająco, ale potrzebuję zapamiętać: 1) Uzyskaną najwyższą punktację ze podobne wyrazy. 2) Nazwę pliku. 3) Listę podobnych wyrazów
  • 8
    C/C++
    if( filtr == NULL ) // (8)
         lista_plikow = listujPlikiPosortowane( sciezka );
    else
         lista_plikow = listujPlikiPosortowane( sciezka, filtr );
    właśnie w tym miejscu jest rozstrzygnięcie którą wersję funkcji wybrać - z czy bez filtra
  • 8b
    C/C++
    for( auto & i: lista_plikow ) { // 8b
        forward_list < string > przeszukiwany = plikNaWyrazy(( string( sciezka ) + "/" + i ).c_str() );
        multimap < unsigned int, string > podobienstwa = szukajPodobienstw( zrodlo, przeszukiwany );
        max_poobnych_wyrazow = pokazPodobienstwa( podobienstwa );
        suma_podobienstw.push_front( make_tuple( max_poobnych_wyrazow, i, podobienstwa ) );
    }
    ta pętla przechodząc po całej liście nazw plików -każdy z plików rozkłada na wyrazy, szuka podobieństw z plikiem źródłowym i dodaje do naszej tupli wymienione w punkcie (7) informacje
  • 8c
    C/C++
    if( max_poobnych_wyrazow >= pow( min_ilosc_punktow_za_wyrazy, 1./ 2.) ) // (8c)
         strumien << "Dla pliku '" << plik_zrodlowy << "' jest zawierajacy " <<( max_poobnych_wyrazow + 1 ) << " punktow za wyrazy, lista:\n";
    else
         strumien << "Dla pliku '" << plik_zrodlowy << "' nie znaleziono zawierajacego co najmniej " << min_ilosc_punktow_za_wyrazy << " punktow za wyrazy\n";

    jest tutaj wyświetlone podsumowanie treści, która pojawi się poniżej, czyli informacji ile punktów za wyrazy jest dla danego pliku. Punkty za wyrazy są liczone jako kwadraty podciągu identycznych wyrazów gdyż przyjąłem że np. 5 wyrazów takich samych obok siebie to znacznie więcej niż 5 takich samych, lecz rozrzuconych po całym tekście. Funkcje zwracają punkty za wyrazy, w związku z tym przeliczanie punktów z powrotem na wyrazy może być nieprecyzyjne
  • 9
    C/C++
    for( auto & i: suma_podobienstw ) { // (9)
        if( get < 0 >( i ) > 0 ) {
            strumien << "PUNKTOW ZA WYRAZY: " << get < 0 >( i ) << " w pliku o nazwie '" << get < 1 >( i ) << "' a oto lista:\n";
            for( multimap < unsigned, string >::iterator it = get < 2 >( i ).begin(); it != get < 2 >( i ).end(); ++it )
            if(( * it ).first >= min_ilosc_punktow_za_wyrazy )
                 strumien << "\t-) " <<( * it ).first << '\t' <<( * it ).second << '\n';
           
        }
    tutaj są wyświetlane podobieństwa o ilości wyrazów pożądanej przez użytkownika uwzględniające: punkty za wyrazy, nazwę pliku, oraz ciągi podobnych wyrazów
  • 10
    szukajPlagiatow( "test.txt", "teksty" );
    Wywołanie funkcji szukającej plagiatów dla pliku o podanej nazwie i dla ścieżki podanej w drugim argumencie

Kody źródłowe wszystkich powyższych programów

Wyrażam zgodę na używanie kodów źródłowych z tego artykułu we własnych programach (nawet komercyjnych), pracach na zakończenie studiów i oczywiście celach dydaktycznych, miło by było gdyby zawarta była wtedy informacja o pochodzeniu kodu z serwisu cpp0x.pl