Pliki

Perl FAQ | RecentChanges | Preferences

1. Jak otworzyć plik?
1.1. > - tylko do zapisu.
1.2. < - tylko do odczytu.
1.3. >> - tylko do dopisywania.
1.4. +>> - odczyt/dopisywanie.
1.5. +< - odczyt/zapis.
1.6. +> - odczyt/zapis.
2. Jak ustawić się w zadanym miejscu?
3. Czy można odczytać/dopisać bez otwierania pliku?
4. Jak dopisać/poprawić w zadanym miejscu?
5. Jak znaleźć pliki w katalogu?
6. Jak wyłuskać nazwę pliku/katalogu?
7. Jak odczytać/zmienić hasło w pliku (dodać użytkownika)?
8. Jak sprawdzić prawa dostępu do pliku?
9. Jak sprawdzić plik wskazywany linkiem symbolicznym?
10. Jak sprawdzić datę modyfikacji pliku?
11. Jak uaktualnić daty modyfikacji/dostępu plików?
12. Jak sprawdzić wielkość (rozmiar) pliku?
13. Jak skopiować/przesunąć plik?
14. Jak mogę usunąć plik?
15. Jak zmniejszyć plik?
16. Jak rozpoznać typ pliku?
17. Jak wylosować linię z pliku
18. Czy można zapisać do pliku całą strukturę danych?
19. Jaka funkcja służy do ograniczania dostępu do pliku?
20. Gdzie znajduje się bieżący katalog (.)?
21. Jak przekierować STDOUT/STDERR?
22. Czym generować RTF?
23. Jak zrobić PDF?
24. Jak obsłużyć plik po kompresji?
25. Jak porównać pliki?
26. Jak policzyć sumę kontrolną CRC?

1. Jak otworzyć plik?

1.1. > - tylko do zapisu.

Jeśli plik nie istnieje, zostanie utworzony. Jeśli istnieje, jego poprzednia zawartość zostanie wymazana.

1.2. < - tylko do odczytu.

Domyślny sposób otwarcia pliku.

1.3. >> - tylko do dopisywania.

Wszelkie informacje są dodawane na końcu pliku.

1.4. +>> - odczyt/dopisywanie.

Jak wyżej z odczytem.

1.5. +< - odczyt/zapis.

Można pisać i czytać w dowolnym miejscu.

1.6. +> - odczyt/zapis.

Jak wyżej. Zawartość pliku zostaje na początku wymazana.

2. Jak ustawić się w zadanym miejscu?

Gdy znane jest miejsce w bajtach należy użyć seek(PLIK, przesuniecie, skad). Parametr skad określa względem czego podajemy przesunięcie

 - od początku pliku (skad = 0)
 - od aktualne pozycji (1)
 - od końca pliku (2)

Gdy nie jest znane miejsce należy go ustalić tworząc indeksy (położenie miejsc charakterystycznych) na przykład w osobnym pliku. Czytanie od zadanej linii jest niemożliwe bez wczytania poprzednich linii.

3. Czy można odczytać/dopisać bez otwierania pliku?

Tak. Ciekawym modułem jest io::all.

 io('file.txt') > $contents;
 $content > io('file.txt');
 $content >> io('file.txt');
 io('copy.txt') < io('file.txt');
 io('more.txt') >> io('all.txt');
 print "$_\n" for io(".")->all(0)

4. Jak dopisać/poprawić w zadanym miejscu?

Ogólnie da się jedynie nadpisywać w środku pliku poprzez zamianę znaków
 (open FH,"<+plik") lub dopisywać na końcu (open FH,">>plik").
W celu dodania lub skasowania czegoś ze środka lub początku pliku, należy przepisać cały plik.

Do dyspozycji jest także konstrukcja jednolinijkowa zamieniająca np. piątą linię:

 perl -pli -e '$_="nowa piąta linia" if $.==5' plik

5. Jak znaleźć pliki w katalogu?

Nie należy stosować konstrukcji uruchamiających program zewnętrzny, które mogą spowalnić działanie (przykładowo pliki z rozszerzeniem .txt):

 @pliki = `ls *.txt`; 
 @pliki = glob('*.txt');

Zamiast niej lepiej użyć:

 opendir(KATALOG,".");
 @pliki = grep { /\.txt$/ } readdir KATALOG;
 closedir KATALOG;

By ustawić znalezione powyżej pliki z rozszerzeniem .txt (pomijając grep'a - wszystkie) w kolejności czasów modyfikacji:

 @pliki = sort { (stat($b))[9] <=> (stat($a))[9] } @pliki;

Do szukania (/usr/home) również w podkatalogach (plików .pm) można użyć systemowego find lub lepiej modułu File::Find.

 use File::Find;
 %found = ( );
 find( \&wanted, '/usr/home' );
 $wynik = join "\n", keys %found;
 print "$wynik\n";

 sub wanted {
   $found{( split "/", $File::Find::name )[4] } = 1 if /\.pm$/; 
 }

Uproszczona wersja tego modułu to File::List. Jest też polecenie systemowe find2perl, konwertujące systemowego find'a na skrypt perlowy.

6. Jak wyłuskać nazwę pliku/katalogu?

Najlepszym i przenośnym rozwiązaniem jest moduł File::Basename. Można też użyć wyrażenia regularnego: /([^\/]+)$/

7. Jak odczytać/zmienić hasło w pliku (dodać użytkownika)?

Moduły/funkcje HTTPD::UserAdmin i HTTPD::GroupAdmin obsługują plik z hasłami w formacie tekstowym, dbm, Berkley DB, lub innym zgodnym z DBI:

 use HTTPD::UserAdmin ();
 HTTPD::UserAdmin
    ->new(DB => "/foo/.htpasswd")
    ->add($username => $password);

Do wczytania hasła z klawiatury dobrze jest użyć modułu Term::ReadKey lub Term::ReadLine, Term::ReadPassword.

 use Term::ReadKey;
 ReadMode('noecho');
 $password = ReadLine(0);
 ReadMode('restore');

Jeśli perl (i system haseł ukrytych) został poprawnie zainstalowany funkcje getpw*() [np. getpwnam()] dają dostęp do odczytania haseł użytkowników. (Przy systemie haseł ukrytych tylko, gdy są wywoływane przez administratora)

 $pwd = (getpwuid($<))[1];
 $salt = substr($pwd, 0, 2);	# salt to pierwsze dwa znaki hasla

 if (crypt($haslo, $salt) ne $pwd) { die "Sorry...\n"; } else { print "ok\n"; }

Zmiana hasła wiąże się ze stworzeniem nowego pliku w odpowiednim formacie, opisanym w manie do passwd i zainstalowaniem go za pomocą opisanej w UNIXowym manie funkcji pwd_mkdb. Hasło należy wcześniej zakodować, przy użyciu funkcji crypt za pomocą wygenerowanego wcześniej (patrz niżej) dwubajtowego salta.

Przydatny może okazać się moduł Authen-PAM.

W przypadku kodowania haseł MD5 zastosowanie znajduje odpowiedni moduł:

 use MD5;
 $md5=new MD5;
 $md5->add("$haselko");
 $digest= $md5->digest();

lub

 use Crypt::PasswdMD5;
 print unix_md5_crypt("alamakota", "QYeZBvQu")."\n";

W zakodowanym haśle salt jest miedzy drugim a trzecim znakiem $.

Do wygenerowania hasła może służyć poniższe.

 @chars = ( "A" .. "Z", "a".."z", 0..9, qw(! @ $ % ^ & * ));
 $string = join("",@chars[ map { rand @chars } ( 1 .. 64 ) ]);

8. Jak sprawdzić prawa dostępu do pliku?

man 2 stat:

 $mode = sprintf "%04o", (stat $file)[2] & 0777;

jest jeszcze dodana stała 0100000 - oznaczająca zwykły plik.

Oprócz stat jest jeszcze funkcja -X (perldoc -f -X) z różnymi sprawdzaniami pliku.

9. Jak sprawdzić plik wskazywany linkiem symbolicznym?

 Do tego mamy funkcję readlink lub moduł File::Spec::Link

10. Jak sprawdzić datę modyfikacji pliku?

 my ($s, $min, $h, $d, $m, $y, $weekday);
 if($filename and -e $filename) {
  ($s, $min, $h, $d, $m, $y, $weekday) =
   localtime((stat(_))[9]);	# juz raz bylo stat, wiec mozna uzyc _
  $y += 1900;
  $m ++;  			# zeby byl z przedzialu 1-12
 }

lub

 use POSIX;
 use File::stat;

 print strftime "%d-%m-%Y", localtime( (stat($filename))->mtime );

11. Jak uaktualnić daty modyfikacji/dostępu plików?

 $teraz=time;
 utime $teraz,$teraz,@lista_plikow;

12. Jak sprawdzić wielkość (rozmiar) pliku?

Jedna z linijek:

 $wielkosc=(stat("nazwa-pliku.txt"))[7];
 $wielkosc= -s "nazwa-pliku.txt";
 $wielkosc=(stat("nazwa-pliku.txt"))->size;

13. Jak skopiować/przesunąć plik?

Można to zrobić na dwa sposoby. Jeśli chodzi o szybkość to można uruchomić zewnętrzny program lub użyć modułu File::Copy. Jeśli chodzi o kontrolę i więcej pisania (i jak twierdzą niektórzy przenośność) to przepisywanie open/read/write/close.

Do zmiany nazwy służy funkcja rename.

14. Jak mogę usunąć plik?

 unlink($filename);

15. Jak zmniejszyć plik?

Jest funkcja "truncate", która pozwala "przyciąć" otwarty plik, np:

 truncate F, 1000;

obcina plik otwarty jako F do 1000 bajtów. Tylko że ta funkcja nie musi działać - musisz przetestować czy w Twoim systemie operacyjnym jest zaimplementowana.

16. Jak rozpoznać typ pliku?

Najlepiej po rozszerzeniu, a gdy go nie ma, przyda się moduł File::MMagic. Do sprawdzenia czy jest binarny/tekstowy - operatory: -T, -B.

17. Jak wylosować linię z pliku

 rand($.) < 1 && ($line = $_) while <>;

18. Czy można zapisać do pliku całą strukturę danych?

Zapisanie do pliku tablicy czy hasza (nawet wielopoziomowego) jest możliwe za pomocą modułu Data::Dumper.

19. Jaka funkcja służy do ograniczania dostępu do pliku?

Do blokowania dostępu do pliku służy funkcja flock().

Funkcja ta nie działa na systemach firmy Microsoft. Zamiast niej stosujemy moduły LockFile::Simple lub File::Lock.

Flock ma dwa parametry: uchwyt pliku i rodzaj operacji.

 use Fcntl ':flock';
 sub lock { flock(MBOX,LOCK_EX); seek(MBOX, 0, 2); }
 sub unlock { flock(MBOX,LOCK_UN); }

 open(MBOX, ">>/usr/spool/mail/$ENV{'USER'}") 
   or die "Can't open mailbox: $!";
 lock();
 print MBOX $msg,"\n\n";
 unlock();

By funkcja flock działała poprawnie plik musi być otwarty do pisania (lub dopisywania), czyli można ograniczać po otwarciu i przed zamknięciem pliku. Zamknięcie pliku znosi ograniczenie.

flock(LOCK_EX) blokuje wszystkie procesy próbujące zrobić flock na tym pliku. flock(LOCK_SH) blokuje procesy próbujące zrobić flock(LOCK_EX) (w domyśle: piszące do pliku), ale nie blokuje procesów wykonujących flock(LOCK_SH). W rezultacie wiele procesów czytających ma jednocześnie dostęp do pliku, ale proces piszący zostanie zablokowany do czasu, aż wszystki czytające zwolnią blokadę.

Flock czeka na odblokowanie, o ile nie dodasz LOCK_NB, wtedy wróci z błędem i możesz na to zareagować.

 (LOCK_SH = 1, LOCK_EX = 2, LOCK_NB = 4, LOCK_UN = 8 - zazwyczaj).

20. Gdzie znajduje się bieżący katalog (.)?

 use Cwd;
 $dir = cwd;

21. Jak przekierować STDOUT/STDERR?

Do przekierowania nigdzie (na null):

 open( STDOUT, ">/dev/null" );
 open( STDERR, ">/dev/null" );

lub:

 close (STDOUT);
 close (STDERR);

22. Czym generować RTF?

Po porównaniu RTF::Document v0.64 i RTF::Writer v1.06, trzeba stwierdzić, że RTF::Writer wydaje się bardziej dopracowany (sam numer wersji mógłby już na to wskazywać). Sam przykładowy skrypt do RTF::Document sieje błędami, tak że na sprawną wersję tego modułu chyba trzeba trochę jeszcze poczekać.

23. Jak zrobić PDF?

Perlem tworzymy plik latexowy, a LaTeX świetnie robi pdfy. Alternatywa: http://xml.apache.org/fop/

24. Jak obsłużyć plik po kompresji?

Moduły:

 Archive::Zip 
 Archive::Tar
 Compress::Bzip2
 Compress::Zlib (do gzipa)
 PerlIO::gzip

25. Jak porównać pliki?

Można skorzystać z modułów: Text::Diff, Data::Diff lub Algorithm::Diff

26. Jak policzyć sumę kontrolną CRC?

Dla całego drzewa katalogów:

 use Digest::MD5;
 $dx = new Digest::MD5;
 subdir($DIR);
 print "MD5($DIR) = ", $dx->hexdigest, "\n";

 sub subdir {
    my $dir = shift;
    opendir(D, $dir);
    my @list = grep { ! /^\.\.?$/ } readdir(D);
    closedir(D);

    for(@list) {
        if (-d "$dir/$_") { subdir("$dir/$_"); }
        elsif (-f _) {
            open(F, "$dir/$_");
            $dx->addfile(F);
            close(F);
        }
    }
 }

Perl FAQ | RecentChanges | Preferences
This page is read-only | View other revisions
Last edited June 13, 2006 3:17 pm by kujon.kt.agh.edu.pl (diff)
Search:
Strona znajduje się na serwerze KT AGH.