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:
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:
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:
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:
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)
#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( "." );
}
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:
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:
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int pokazErrno();
void listujPlikiLadnie( const char * nazwa_sciezki, unsigned ilosc_w_linii, unsigned rozmiar_bufora ) {
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 )
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() {
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) 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 );
}
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:
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):
#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 ) {
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 );
if( argc > 1 )
listujPlikiPosortowane( argv[ 1 ] );
else
listujPlikiPosortowane( "." );
cout << "\nj.w. ale w linii, bez liczenia:\n";
listujPlikiPosortowane( ".", 1, 50, false );
}
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:
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):
#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 );
}
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".
#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 );
}
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.:
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ć.
#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 ) {
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( "." ) );
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 ) {
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;
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 ) {
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 ) {
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 ) {
out.push_back( make_tuple( nazwa, adres, tmp ) );
break;
}
}
}
return out;
}
virtual unsigned long long rozmiar( int( * filtr )( const Element & ) = NULL ) {
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 ) {
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 ) )
{
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() );
}
return out;
}
static void wyswietlTuplePlikow( list < tuple < string, string, string > > pliki, ostream & os = cout, string szukana = "" ) {
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( "/" ) );
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 );
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 ) {
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' );
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 ) {
return( plik->d_type ) == DT_DIR;
}
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:
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
#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 );
}
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
#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;
}
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#include "plikowanie.hpp"
main() {
Katalog k( "." );
string szukana( "gcc " );
Katalog::wyswietlTuplePlikow( k.plikiZawierajaceWTresci( szukana ), cout, szukana );
}
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><cassert></code>, <code><cstdlib></code>, <code><new></code> and <code><exception></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
#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();
}
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
#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();
}
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
#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;
}
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:
#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";
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 );
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" ) {
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 ) {
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 ) {
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 * ) ) {
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 ) {
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;
if( filtr == NULL )
lista_plikow = listujPlikiPosortowane( sciezka );
else
lista_plikow = listujPlikiPosortowane( sciezka, filtr );
for( auto & i: lista_plikow ) {
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.) )
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 ) {
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" );
}
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:
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 |