Źródło: Nik Silver. Perl Tutorial: Start. http//:agora.ac.ic.uk/nik/ - strona już nie istnieje.
Zachował się oryginalny tekst Perl Tutorial. Więcej informacji zawiera nowy podręcznik Perl-a na stronie Tutorials Point (html) lub (PDF) .


Tekst ten rozpoczyna naukę perla, i początkowo był wykorzystany przez Nika Silvera jako pierwsza część praktycznych warsztatów, prowadzonych w Szkole Nauk Komputerowych na Uniwersytecie w Leeds w Zjednoczonym Królestwie. Druga część warsztatów dotyczyła projektowania stron WWW. perla wykorzystano do obsługi przycisków oraz obszarów tekstowych i generowania zwykłych stron WWW w locie. Budowanie stron WWW bez tego wszystkiego jest o wiele prostsze. W Leeds znajdują się informacje o tym jak to należy robić, lepiej jednak zajrzeć do kompletnego przewodnika dla początkujących (Beginner's Guide) NCSA.

Istnieje wiele innych materiałów do nauki perla. Prawie wszystkie można znaleźć w Archiwum perla w UF/NA. Chciałbym jednak aby znalazły się tutaj ćwiczenia dotyczące zwartej temetyki, a żaden inny tekst tego nie zapewnia.

Zakładam bardzo podstawową wiedzę o systemie UNIX  koncentrując się na tym jak pisać podstawowe, działające programy perla. Nie wyjaśniam dlaczego coś działa tak jak działa, jak również nie dokonuje rozszerzeń języka; zakładam, że czytelnik może sam we własnym zakresie dokonać właściwych dla siebie rozszerzeń.

Dziękuje Neilowi Bowersowi z którego strony o perlu buchnąłem ikone wielbłąda (on pewnie też ją buchnął wcześniej od kogoś innego) oraz naszemu Zespołowi Pomocniczemu za techniczne umiejętności.

Podstawowy program

Oto podstawowy, nieco już nudny, program w perlu.
      #!/usr/local/bin/perl
      #
      # zwyczajny program
      #
      print 'Witaj świecie.';  # drukuj
      
Przedyskutujmy kolejne fragmenty programu.

Pierwsza linia

Każdy program perla rozpoczyna się w systemie UNIX w ten sposób. W systemie DOS jest inaczej. Jeśli program perl znajduje się w innym katalogu niż /usr/local/bin wówczas należy wypisać w tym miejscu nazwę tego katalogu. Informacja w pierwszej inii mówi systemowi operacyjnemu, że następujący po niej program należy przepuścić przez program perl.

Komentarze i instrukcje

Komentarze wstawiamy do programu poprzedzając je znakiem #. Wszystko co następuje po znaku # aż do końca linii jest ignorowane przez perl. Cały pozostały tekst to instrukcje dla programu perl. Wszystkie kończą się średnikiem.

Proste wydruki

Funkcja drukująca print wypisuje określone informacje. W omawianym przypadku jest to łańcuch Witaj świecie. Instrukcja print jest również zakończona średnikiem.

Uruchamianie programu

Napisz powyższy program używając edytora tekstu i zapamiętaj go. Możesz w tym celu użyć edytora emacs, który posiada specjalny mod formatowania programu dla perla (użyj 'M-x perl-mode'). Najlepiej jednak używać znanego sobie edytora tekstu. Po zapisaniu programu należy uczynić go wykonywalnym, pisząc (UNIX)
        chmod u+x nazwaprogramu
      
gdzie nazwaprogramu jest nazwą programu (pliku, w którym jest on zapisany). Następnie należy napisać jedno z poniższych poleceń
        perl nazwaprogramu
        ./nazwaprogramu
        nazwaprogramu
      
W systemie DOS wydajemy pierwsze z wypisanych poleceń. Jeśli program zawiera błędy możemy nic z niego nie dostać. Dobrze jest wtedy uruchomić program poleceniem
        perl -w nazwaprogramu
      
W przypadku błędów zostaną one wypisane zanim program zostanie wykonany. Można też uruchomić perla z tzw. odpluskwiaczem (debuger)
        perl -d nazwaprogramu
      
Wykonując program perl dokonuje najpierw jego translacji i w dalszej kolejności wykonywana jest ta postać programu. Wyjaśnia to dlaczego czasami pojawiają się błędy kompilacji.

Zmienne skalarne

Podstawowym rodzajem zmiennych są w perlu zmienne skalarne. W zmiennych skalarnych przechowywane mogą być łańcuchy i liczby. Można to robić zamiennie. Np. instrukcja
        $priorytet=9;
      
umieszcza wartość 9 w zmiennej skalarnej $priorytet. Pod tę samą zmienną można również podstawić łańcuch
        $priorytet='wysoki';
      
perl akceptuje też liczby w postaci łańcuchów, np.
        $priorytet='9';
        $priorytet='009';
      
i nadal radzi sobie z arytmetyką i innymi operacjami. W ogólności nazwy mogą zawierać liczby, litery i podkreślenie (underscore, _), lecz nie mogą rozpoczynać się od liczby. Jak zobaczymy dalej zmienna $_ jest zmienną specjalną. Prócz tego perl rozróżnia wielkość liter, a więc $a i $A oznaczają różne zmienne.

Operacje i przypisania

perl, podobnie jak C, używa zwyczajnych operatorów arytmetycznych.
        $a = 1 + 2;      # dodaj 1 do 2 i umieść wynik w $a
        $a = 3 - 4;      # odejmij 4 od 3 i umieść wynik w $a
        $a = 5 * 6;      # pomnóż 5 przez 6, umieść wynik w $a
        $a = 7 / 8;      # podziel 7 przez 8; wynik w $a
        $a = 9 ** 10;    # podnieś 9 do potęgi 10
        $a = 5 % 2;      # reszta z dzielenia 5 przez 2
        ++$a;            # zwiększ $a o 1 i zwróć wynik
        $a++;            # zwróć $a i następnie zwiększ $a o 1
        --$a;            # zmniejsz $a o 1
        $a--;            # zwróć $a i zmniejsz ją o 1
      
Następujące operacje dotyczą łańcuchów:
        $a = $b . $c;    # łączenia $b i $c; konkatenacja
        $a = $b x $c;    # $a powtórzone $c razy
      
Przypisania w perlu:
        $a = $b;         # przypisz $b do $a
        $a += $b;        # dodaj $b do $a
        $a -= $b;        # odejmij $b od $a
        $a .= $b;        # przyłącz $b do $a
      
Zauważymy, że perl dokonując przypisania (podstawienia) poprzez $a = $b wykonuje najpierw kopię $b, a następnie podstawia ją pod $a. Kiedy następnie $b jest zmieniane nie wpływa to na $a. Inne operatory perla można znaleźć w manualu perlop. Wypisz polecenie man perlop.

Interpolacja

Następujący kod drukuje gruszki i banany używając konkatenacji.
        $a = 'gruszki';
        $b = 'banany';
        print $a . ' i ' . $b;
      
Wygobniej byłoby wypisywać pojedynczy łańcuch w końcowej instrukcji print, lecz linia
        print '$a  i $b';
      
wypisuje literał $a i $b co nie jest naszym celem. Zamiast tego można użyć podwójnych cudzysłowów zamiast apostrofów
        print "$a  i $b";
      
Podwójne cudzysłowy wymuszają interpolację (przekształcanie) wyrażeń w nich zawartych, włączając w to interpretację zmiennych. Sposób ten jest o wiele wygodniejszy niż użyty na początku. Innymi kodami, które są interpolowane, są znaki specjalne takie jak np. znak nowej linii \n oraz znak tabulacji \t.

Ćwiczenie

Przepisz program "Witaj świecie." następująco. (a) Przypisz łańcuch do zmiennej (b) wypisz tę zmienną wraz ze znakiem nowego wiersza. Użyj podwójnego cudzysłowu i nie używaj operatora konkatenacji. Zanim przejdziesz dalej upewnij się, że umiesz to zrobić.

Zmienne tablicowe

Bardziej interesującymi zmiennymi są zmienne tablicowe, które są listami skalarów (a więc łańcuchów i liczb). Zmienne tablicowe posiadają taki sam format jak zmienne skalarne z tym wyjątkiem, że są poprzedzone symbolem @. Instrukcje
        @owoce={"gruszki","banany","porzeczki"};
        @muzyka={"gwizdek","flet"};
      
przypisują zmiennej tablicowej @owoce trzyelementową listę skalarów, a zmiennej @muzyka listę dwuelementową.

Dostęp do tablicy uzyskujemy podając indeks o wartości 0, 1, 2, ..., w nawiasie kwadratowym. Wyrażenie $owoce[2] zwraca porzeczki. Zauważmy, że znak @ zastąpiony został przez $ ponieważ porzeczki to skalar.

Przypisania tablic

Jak wszędzie w perlu, to samo wyrażenie w zależności od kontekstu, może prowadzić do innych wyników. Pierwsze przypisanie poniżej, rozdyma zmienną @muzyka tak, że staje się równoważne przypisaniu drugiemu.
      @orkiestra={"organy",@muzyka,"harfa"};
      @orkiestra={"organy","gwizdek","flet","harfa"};
    
Sugeruje to sposób dodawania elementów do tablic. Elegancki sposób dodawania elementów polega na wydaniu polecenia
      push{@owoce,"truskawki"};
    
które umieszcza truskawki na końcu tablicy @owoce. Aby dodać dwa lub więcej elementów do tablicy można użyć któregoś z poleceń
      push(@owoce,"truskawki","kiwi");
      push(@owoce.("truskawki","kiwi"));
      push(@owoce,@inneowoce);
    
Funkcja push zwraca długość nowej listy.

Aby usunąć ostatni element tablicy używamy funkcji pop.

      $pop(@owoce);
    
Lista zawiera o jeden element mniej.

Możliwe jest przypisanie tablicy zmiennej skalarnej. Wynik przypisania zależy od kontekstu. Linia

      $f = @owoce;
    
przypisuje długość listy @owoce zmiennej $f, natomiast
      $f = "@owoce";
    
zamienia listę na łańcuch nazw oddzielonych znakiem spacji. Znak spacji można zamienić na dowolny znak zmieniając zawartość specjalnej zmiennej $". Jest to jedna z wielu dziwnych zmiennych specjalnych perla.

Tablic można również używać w instrukcjach wielokrotnego przypisania.

      ($a, $b) = ($c, $d);    # to samo co $a = $c; $b = $d;
      ($a, $b) = @owoce;      # $a i $b są takie same jak 
      # pierwszy i drugi element @owoce;
      ($a, @inne) = @owoce;   # $a jest pierwszym elementem @owoce
      # @inne zawiera pozostałe elementy @owoce
      (@inne, $a) = @owoce    # @inne = @owoce; $a jest niezdefiniowana
    
Ostatnie przypisanie ma miejsce z powodu chciwej natury tablic. Tablica @inne pożera taką część tablicy @owoce jaka jest tylko możliwa. Z tego powodu należy unikać tej postaci przypisania.

Jeśli w końcu chcemy znać indeks ostatniego elementu tablicy, wydajemy polecenie

      $#owoce
    

Wypisywanie tablic

Ponieważ kontekst jest istotny, więc nie jest zbyt dziwnym, że wszystkie poniższe polecenia prowadzą do różnych wyników.
      print @owoce;      # 
      print "@owoce";    # w dwu podwójnych cudzysłowach
      print @food."";    # kontekst skalarny
    

Ćwiczenie

Sprawdź co powodują przedstawione powyżej polecenia drukowania tablic.

Pliki

Przedstawiony poniżej program perla działa tak samo jak polecenie cat UNIXa.
      #!/usr/local/bin/perl
      #
      # program otwiera plik password, czyta w nim,
      # drukuje jego zawartość i zamyka go

      $plik = '/etc/passwd';       # nazwij plik
      open(INFO,$plik);            # otwórz plik
      @linie=<INFO>;               # wczytaj go do tablicy
      close(INFO);                 # zamknij plik
      print @linie;                # drukuj tablicę
    
Funkcja open otwiera plik do czytania. Pierwszy parametr jest uchwytem pliku, poprzez który perl może się następnie odwoływać do pliku. Drugi parametr oznacza nazwę pliku. Jeśli nazwa pliku jest podana w apostrofach wówczas jest interpretowana dosłownie bez dodatkowej interpretacji powłoki. Wyrażenie, np. '/notatki/listaprac' nie będzie więc dalej interpretowane. Jeśli życzymy dodatkowej interpretacji przez powłokę musimy ując to w nawiasy trójkątne, a więc </notatki/listaprac>.

Funkcja close mówi by perl zakończył pracę z tym plikiem.

W związku z tą dyskusją warto dodać kilka uwag dotyczących używania uchwytów plików. Po pierwsze w poleceniu open można wyspecyfikować czy plik ma być plikiem do zapisu, do dopisywania czy też tylko do odczytu. W tym celu poprzedzamy nazwę pliku znakiem > - zapis, lub znakiem >> - dopisywanie do pliku.

        open(INFO, $plik);          # otwórz do odczytu
        open(INFO, ">$plik");       # otwórz do zapisu
        open(INFO, ">>$plik");      # otwórz w celu dopisywania
        open(INFO, "<$plik");       # otwórz do odczytu; jak w pierwszej linii
      

Po drugie, jeśli chcemy zapisać coś do uprzednio otwartego do zapisu pliku, używamy polecenia print z dodatkowym parametrem

        print INFO "Ta linia zapisana jest do pliku.\n"
      

Po trzecie, do otwarcia standardowego pliku wejścia (zazwyczaj klawiatura) i wyjścia (zazwyczaj ekran monitora) piszemy:

          open(INFO, '-');          # otwórz standardowe wejście
          open(INFO, '>-');         # otwórz standardowe wyjście
        
W wyżej podanym programie informacje czytane są z pliku. Plkiem jest INFO i do czytania go perl używa nawiasów trójkątnych. Polecenie
          @linie = <INFO>;
        
czyta plik, którego uchwytem jest INFO do tablicy @linie. Wyrażenie <INFO> powoduje czytanie całego pliku w jednym przebiegu. Dzieje się tak dlatego, że plik wczytywany jest do tablicy. Jeśli zastąpimy tablicę @linie przez skalar $linie wówczas perl przeczyta tylko jedną linię (rekord) danych. W obu wypadkach rekord zapamiętywany jest wraz ze znakiem nowej linii na końcu.

Ćwiczenie

Zmień powyższy program tak, by cały plik był drukowany ze znakiem # na początku każdej linii. Dodaj tylko jedną nową linię i zmień inną. Użyj zmiennej $". Ponieważ przy pracy z plikami mogą wystąpić niespodziewane komplikacje, użyj opcji -w, zgodnie z dyskusją w rozdziale o uruchamianiu programu perla.

Konstrukcje kontrolne

Bardziej interesujące możliwości pojawią się gdy wprowadzimy struktury kontrolne i pętle. perl dostarcza wielu różnych konstrukcji kontrolnych podobnych do występujących w C lub w Pascalu. Przedyskutujemy kilka z nich.

foreach

Struktura foreach służy do przeglądania tablic lub innych danych o charakterze list. Jest ona postaci
        foreach $produkt (@owoce)     # odwiedź kolejno każdy element
        # i nazwij go $produkt
        {
          print "$produkt\n";         # wydrukuj element
          print "Niam, niam.\n"       # To jest coś
        }
      
Działania, które są podejmowane za każdym razem, są zawarte w nawiasach klamrowych. Przy pierwszym przebiegu zmienna $produkt przyjmuje wartość pierwszego elementu tablicy @owoce, w następnym drugiego itd., aż do ostatniego. Jeśli tablica jest pusta wówczas blok {...} nie jest wykonywany.

Testy

Następujące struktury dają w wyniku prawdę lub fałsz. W perlu każda liczba różna od zera lub niepusty łańcuch są interpretowane jako prawda (true). Liczba zero, zero jako łańcuch i łańcuch pusty są interpretowane jako fałsz (false).
        $a == $b;      # czy $a jest liczbowo równe $b?
        # Ostrzeżenie: nie używaj operatora =
        $a != $b;      # czy $a jest liczbowo różne od $b?
        $a eq $b;      # czy $a jako łańcuch równe jest $b? 
        $a ne $b;      # czy $a jako łańcuch jest różne od $b?
      
Można też używać logicznych operatorów and, or oraz not:
        ($a && $b);            # czy prawda, że $a i $b?
        ($a || $b);            # czy prawda, że $a lub $b?
        !($a);                 # czy nie $a? 
      

for

W perlu podobnie jak w C, wbudowana jest struktura for.
        for (inicjacja; warunek; przyrost)
        {
          pierwsze_polecenie;
          drugie_polecenie;
          etc
        }
      
Na początku wykonywane jest polecenie inicjacja, następnie, jeśli warunek jest spełniony (prawda), wówczas wykonywany jest blok {...}. Za każdym wykonaniem bloku obliczane jest wyrażenie przyrost. Oto przykład pętli drukującej liczby od 0 do 9.
        for ($i = 0; $i < 10; ++$i)   # zacznij z $i=1
                                      # rób dopóki $i < 10
        {
           print "$i\n";
        }
      

while i until

Poniżej pokazany jest program, który czyta dane z klawiatury i jest powtarzany aż do momentu gdy podane jest prawidłowe hasło
        #!/usr/local/bin/perl
        print "Password? ";       # prośba o dane
        $a = <STDIN>              # pobierz dane
        chop $a;                  # usuń znak new line na końcu linii
        while ($a ne "fredek")    # jeśli dane są złe ...
        {
          print "Przykro mi. Jeszcze raz? "; # jeszcze raz
          $a = <STDIN>>                      # pobierz dane
          chop $a;                           # usuń znów nl
        }
      
Blok {...} jest wykonywany zawsze gdy dane są niezgodne z hasłem (password). Zauważmy, że po pierwsze można czytać dane bez otwierania standardowego wejścia (klawiatura). Po drugie, kiedy wpiszemy prawidłowe hasło (fredek) wówczas wczytywane jest ono wraz ze znakiem nowej linii (nl). Funkcja chop usuwa ostatni znak rekordu, którym jest w tym wypadku nl.

Chcąc sprawdzić coś przeciwnego powinniśmy użyć polecenia until dokładnie w ten sam sposób. Blok wykonywany jest dopóty dopóki wyrażenie jest prawdą.

Inną użyteczną techniiką jest umieszczenie warunku while lub until na końcu instrukcji blokowej. Wymaga to obecności operatora do, który wskazuje początek bloku i umieszczenia warunku na końcu bloku. Powyższy program można zapisać następująco.

        #!/usr/local/bin/perl
        do
        {
          print "Password? ";       # prośba o dane
          $a = ;             # pobierz dane
          chop $a;                  # usuń znak new line na końcu linii
        }
        while ($a ne "fredek")      # powtórz jeśli dane są złe ...
      

Ćwiczenie

Zmień program z poprzedniego ćwiczenia tak by każda linia była czytana i następnie drukowana z kolejnym numerem na początku. Wynik ma wyglądać podobnie do
        1 root:oYpYXm/qR06n2:0:0:Super-User:/:/bin/csh
        2 sysadm:*:0:0:System V Administration:/usr/admin://bin/sh
        3 diag:*:0:996:Hardware Diagnostics:/usr/diags:/bin/csh
        etc.
      
Możesz wykorzystać konstrukcję
        while ($linia = <INFO>)
        {
        ...
        }
      
Jeśli to wykonałeś sprawdź, czy możesz zmienić tak program by numery linii były drukowane wg. wzoru 001, 002, ..., 009, 010, 011, 012, etc. Aby to wykonać możesz zmienić dodatkowo tylko cztery znaki. perl to potrafi.

Instrukcje warunkowe

perl dostarcza również instrukcji if/then/else. Ich postać jest następująca:
        if ($a)
        {
          print "Łańcuch nie jest pusty.\n";
        }
          else
        {
          print "Łańcuch jest pusty.\n";
        }
      
Należy tutaj pamiętać, że łańcuch pusty oznacza fałsz. Wynik negatywny otrzymuje się też jeśli łańcuch jest 0.

W instrukcji warunkowej można zawrzeć więcej możliwości

        if (!$a)
        {
          print "Łańcuch jest pusty.\n";
        }
          elsif (length($a)==1)
        {
          print "Łańcuch zawiera jeden znak.\n";
        }
          elsif (length($a)==2)
        {
          print "Łańcuch zawiera dwa znaki.\n";
        }
          else
        {
          print "Łańcuch zawiera wiele znaków.\n";
        }
      
Należy zauważyć, że instrukcja elsif nie zawiera ë.

Ćwiczenie

Znajdź plik, który zawiera dużo tekstu oraz puste linie. Zmień poprzedni program, który pracuje z plikiem passwd i wypisuje numery lini tekstu tak, by pracował z dowolnym plikiem. Zmień go tak by numerowane były tylko linie tekstu (numerty pustych linii pomijamy) i by drukowane były wszystkie linie. Pamiętaj o tym, że każda wczytana linia kończy się znakiem nowej linii.

Dopasowywanie łańcuchów

Jedną z najbardziej użytecznych funkcji perla (jeśli nie najważniejszą) jest manipulowanie łańcuchami. Istotą tego są, podobnie jak w innych programach UNIXa, wyrażenia regularne (WR lub RE (regular expressions)) lub wzorce.

Wyrażenia regularne

Wyrażenie regularne zawarte jest w ukośnikach (slash). Dopasowanie dokonuje się za pomocą operatora  =. Następujące wyrażenie ma wartość prawda gdy łańcuch ten pojawi się w zmiennej $zdanie.
        $zdanie =~ /to/
      
WR rozróżniają wielkość liter. Zmienna
        $zdanie =~ "To jest szybki rudy lis.";
      
nie zawiera łańcucha to. Operator używany jest w celu stwierdzenia braku dopasowania. W powyższym przykładzie, stwierdzenie

        $zdanie !~ /to/
      
jest prawdą ponieważ łańcuch to tutaj nie występuje.

Zmienna specjalna $_

Można używać konstrukcji warunkowej następująco
        if ($zdanie =~ /na dole/)
        {
          print "Rozmawiamy o rugby\.";
        }
      
Spowoduje to wydruk komunikatu jeśli zmienna $zdanie jest
        $zdanie = "Na górze i na dole";
        $zdanie = "Na górze róże na dole fiołki";
      
Wygodniej jest jeśli zdanie przypisane jest zmiennej $_, która jest zmienną specjalną perla. Powyższy warunek można zapisać następująco:
  if (/na dole/)
  {
     print "Rozmawiamy o rugby\n";
  }
Zmienna $_ jest opcjonalnie używana w wielu operacjach perla.

Więcej o WR

WR mogą składać się z wielu znaków specjalnych. Czyni to WR bardzo skomplikowanymi i jednocześnie nadaje im mocy. Buduj swoje WR powoli. Tworzenie ich jest rodzajem sztuki.

Oto niektóre specjalne znaki WR wraz z ich znaczeniami.

  .         # dowolny znak wyłączając znak nowej linii
  ^         # początek linii lub łańcucha
  $         # koniec linii lub łańcucha
  *         # zero lub więcej razy ostatni znak
  +         # raz lub więcej ostatni znak
  ?         # zero lub jeden raz ostatni znak
A oto przykłady dopasowań. Należy je umieścić między ukośnikami /.../.
  t.k       # t i następnie cokolwiek oraz k
            #   pasuje to do tak, tek, tik, tuk, ...
            #   lecz nie do te, tolo
  ^f        # f na początku linii
  ^ftp      # ftp na początku linii
  e$        # e na końcu linii
  tle$      # tle na końcu linii
  und*      # und plus zero lub więcej d
            #   pasuje do un, und, undd, unddd (etc)
  .*        # wszystko z wyjątkiem new line.
            # Jest tak dlatego, iż . pasuje do wszystkiego
            #   z wyjątkiem nowej linii, a * oznacza zero lub
            #   więcej razy
  ^$        # linia pusta
Istnieje jeszcze więcej możliwości. Nawiasy kwadratowe używane są do dopasowywania znaków z ich wnętrza. Znak - umieszczony w takich nawiasach oznacza pomiędzy, a znak ^ na początku znaczy nie.
  [qjk]      # q lub j lub k
  [^qjk]     # ani q ani j ani k
  [a-z]      # wszystko pomiędzy a i z włącznie
  [^a-z]     # wszystko prócz małych liter
  [a-zA-Z]   # dowolna litera
  [a-z]+     # każdy niepusty ciąg małych liter
W tym miejscu możesz prawdopodobnie opuścić wszystko aż do końca oprócz ćwiczeń. Pozostała część rozdziału ma charakter odsyłacza.

Pionowa kreska | reprezentuje or, a nawiasy są używane aby zgrupować różne rzeczy.

  galaretka|krem  # galaretka albo krem
  (eg|le)gs       # albo egs albo legs
  (da)+           # da lub dada lub dadada lub ...
A oto inne znaki specjalne:
  \n           # nowa linia
  \t           # tabulator
  \w           # znak alfanumeryczny (słowo)
               # to samo co [a-zA-Z0-9_]
  \W           # wszystko co nie jest słowem
               # to samo co [^a-zA-Z0-9_]
  \d           # cyfra. To co [0-9]
  \D           # nie cyfra, [^0-9]
  \s           # znak biały: odstęp
               # nowa linia, tabulator, etc
  \S           # nie spacja
  \b           # ogranicznik słowa, tylko wewnątrz []
  \B           # zaprzeczenie powyższego
Oczywiście, znaki takie jak $, |, [, ), \, / itd. są szczególnymi znakami w wyrażeniach regularnych. Jeśli chcemy dopasować jakiś z nich musimy poprzedzić go znakiem lewego ukośnika \
  \$           # znak dolara $
  \|           # kreska pionowa
  \[           # otwierający nawias kwadratowy
  \)           # nawias zamykający
  \*           # gwiazdka
  \\           # ukośnik lewy
  \/           # ukośnik prawy

Przykłady WR

Jak już zauważyliśmy wcześniej powinniśmy budować własne W powoli. Oto kilka przykładów wyrażeń regularnych. Aby używać ich do dopasowań należy je umieścić w nawiasach /.../.
  [01]         # albo "0" albo "1"
  \/0          # dzielenie przez zero: "/0"
  \/ 0         # dzielenie przez zero z odstępem: "/ 0"
  \/\s0        # dzielenie  przez zero ze znakiem białym:
               # "/ 0" gdzie spacja oznacza tabulator etc.
  \/ *0        # dzielenie przez zero z możliwymi odstępami:
               # "/0" lub "/ 0", lub "/  0" etc.
  \/\s*0       # dzielenie przez zero z możliwością białych
               # znaków.
  \/\s*0\.0*   # jak wyżej, lecz z kropką dziesiętną
               # i możliwymi zerami po niej. Dopuszczalne
               # są "/0." i "/0.0", i "/0.00", ..., etc.
               # oraz "/ 0." i "/  0.0", i "/   0.00",..., etc.

Ćwiczenie

Poprzednio twój program zliczał niepuste linie. Zmień go tak by zliczał tylko linie zawierające W każdym przypadku program powinien wypisać wszystkie linie i powinien ponumerować tylko te wyspecyfikowane. Spróbuj użyć zmiennej $_ by uniknąć używania operatora dopasowania = ~ .

Podstawienia i translacje

perl, identyfikując wyrażenia regularne, może dokonywać podstawień (zamian) opartych na tego rodzaju dopasowaniach. Dokonuje się tego przy użyciu funkcji s, która jest zaprojektowana tak, by naśladować sposób zamiany w edytorze vi. Operacja dopasowania działa domyślnie na zmiennej $_, lub z wykorzystaniem operatora dopasowania na innych zmiennych.

W celu zamiany łańcuch alondyn na Londyn w zmiennej $zdanie, używamy polecenia

  $zdanie =~ s/londyn/Londyn/
Aby dokonać tego ze zmienną $_ piszemy
  s/londyn/Londyn/
Zauważmy, że oba wyrażenia regularne (londyn i Londyn) są zawarte łącznie w trzech ukośnikach. Wynik wyrażenia jest liczbą dokonanych zamian. W tym przypadku wyrażenie jest równe albo 0 (fałsz) albo 1 (prawda).

Opcje

W tym przykładzie dokonywana jest co najwyżej jedna zamiana tekstu. Może się zdarzyć, że chcemy wykonać więcej takich zamian. W celu dokonania podstawienia globalnie musimy po ostatnim ukośniku umieścić g, tak jak tutaj
  s/londyn/Londyn/g
co oczywiście dotyczy zmiennej $_. Wynikiem jest jak poprzednio liczba dokonanych zamian, a więc 0 (fałsz) albo więcej niż zero (prawda).

Jeśli chcemy by zamienione zostały ciągi lOndyn lonDYN, LoNDyN itd. możemy napisać

  s/[lL][oO][nN][dD][yY][nN]/Londyn/g
Łatwiej jest to zrobić używając w tym celu opcji i (ignoruj wielkość liter).,/P.
  s/londyn/Londyn/gi
Wyrażenie to dokona globalnej zamiany z pominięciem wielkości liter wzorca. Opcja i może być również używana w podstawowym wyrażeniu regularnym /.../.

Zapamiętywanie wzorców

Wygodnie jest czasami zapamiętać dopasowane wzorce tak, by można je było używać później. Tak się składa, że wszystko co zostało dopasowane w nawiasach zapamiętywane jest w zmiennych $1, $2, ..., $9. Zmienne te można wykorzystać w tych samych wyrażeniach regularnych (lub podstawieniach), używając specjalnych kodów WR \1, ..., \9. Na przykład:
  $_ = "Lord Whopper of Fibbing";
  s/([A-Z])/:\1:/g;
  print "$_\n";
zamieni każdą dużą literę na tę samą literę z dwukropkami po obu jej stronach. Dostaniemy wydruk :L:ord :W:hopper of :F:ibbing. Zmienne $1, ..., $9 są zmiennymi tylko do odczytu. Nie mogą być zmienione na życzenie.

W innym przykładzie, test

  if (/(\b.+\b) \1/)
  {
      print "Znaleziono powtórzone $1\n"
  }
znajdzie powtórzenia dowolnego słowa. Każde \b reprezentuje granicę słowa, a .+ pasuje do każdego niepustego łańcucha, i tak \b.+\b pasuje do wszystkiego pomiędzy dwoma ogranicznikami słowa. Jest to następnie zapamiętane dzięki nawiasom ( ) i zapisane jako \1 dla wyrażenia regularnego i $1 dla reszty programu.

Następujący fragment zamienia pierwszy i ostatni znak w zmiennej $_:

  s/^(.)(.*)(.)$/\3\2\1/
Znaki ^ i $ dopasowują początek i koniec linii. Kod \1 powoduje zapamiętanie pierwszego znaku; \2 zapamiętuje wszystko z wyjątkiem ostatniego znaku, który umieszczony zostaje w \3. Cała linia jest dalej zapisywana tak, że \3 oraz \1 są zamienione miejscami.

Po dopasowaniu można używać zmiennych (tylko do odczytu) $`, $& i $', które zawierają kolejno ciąg przed dopasowanym ciągiem, dopasowany ciąg oraz ciąg występujący po dopasowaniu. Po wykonaniu fragmentu

  $_ = "Lord Whopper of Fibbing";
/pp/;
wszystkie poniższe wyrażenia są prawdziwe (Przypomnijmy, że eq oznacza operatora porównywania łańcuchów.)
  $` eq "Lord Who";
  $& eq "pp";
  $' eq "er of Fibbing";

W związku z zapamiętywaniem wzorców warto wiedzieć, że zmienne wewnątrz ukośników są interpolowane. Kod

  $szukaj = "tam";
  s/$szukaj/xxx/g;
zastąpi więc każde wystąpienie tam przez xxx. Jeśli chcemy zastąpić każdy ciąg tamten przez xxx to nie możemy tego zrobić pisząc s/$szukajten/xxx/ ponieważ interpolacja da $szukajten. Zamiast tego powinniśmy umieścić nazwę zmiennej w nawiasach sześciennych {...}. Oto odpowiedni fragment programu
  $szukaj = "tam";
  s/{$szukaj}ten/xxx/g;

Translacja (Przekład)

Funkcja tr pozwala na translację znak po znaku. Następujące wyrażenie zamienia każde a na e, b na d i każde c na f w zmiennej $zdanie. Wartość wyrażenia jest równa liczbie wykonanych zamian.
  $zdanie =~ tr/abc/edf/;
Większość kodów specjalnych w wyrażeniach regularnych nie stosuje się w funkcji tr. Na przykład, poniższe polecenie zlicza gwiazdki w zmiennej $zdanie i umieszcza wynik w zmiennej >$liczba
  $liczba = ($zdanie =~ tr/*/*/);
Myślnik jednak wciąż oznacza zakres. Następne polecenie zamienia wszystko w $_ na duże litery.
  tr/a-z/A-Z/;

Ćwiczenie

Twój ostatni program powinien zliczać te linie, które zawierają konkretny znak. Zmień go tak by zliczał linie zawierające podwójne litery (lub inne podwójne znaki). Zmień go jeszcze raz tak by podwójne litery były drukowane w nawiasach. Przykładowo, program powinien drukować cos takiego
  023, Amp, James Wa(tt), Bob Transformer, etc. These pion(ee)rs
Zrób tak by wszystkie podwójne litery w linii były wzięte w nawiasy, a nie tylko pierwsza para.

Aby uatrakcyjnić nieco ten program możesz spróbować czegoś takiego. Załóżmy, że program nazywa się liczlinie. Można go więc wywołać przez

  ./liczlinie
Jeśli wywołasz program z parametrami tak jak np. tutaj
  ./liczlinie pierwszy drugi etc
to parametry te są zapamiętywane w specjalnej tablicy @ARGV. W podanym przykładzie pierwszy element tablicy $ARGV[0] jest równy pierwszy, drugi element $ARGV[1] to drugi, a trzeci, a więc $ARGV[2] to etc. Zmień tak swój program by można go było wywołać z jednym argumentem i aby zliczał on tylko te linie, które zawierają łańcuch równy podanemu argumentowi. Program powinien drukować każde wystąpienie podanego łańcucha w nawiasach. Np. program wywołany tak
  ./liczlinie the
powinien dać coś w rodzaju

  019 But (the) greatest Electrical Pioneer of (the)m all was Thomas Edison, who

split

Bardzo użyteczną funkcją perla jest split. Dzieli ona łańcuch i umieszcza jego fragmenty w tablicy. Funkcja używa wyrażeń regularnych i jak zwykle, może pracować ze zmienną $_ chyba, że wskażemy jej inną zmienną.

Funkcji split używamy następująco


  $info = "Caine:Michael:Actor:14, Leafy Drive";
  @personal = split(/:/, $info);
Wynik jej działania jest taki sam jak w przypadku podstawienia

@personal = ("Caine", "Michael", "Actor", "14, Leafy Drive");

Jeśli informacja znajduje się w zmiennej $_ możemy napisać


  @personal = split(/:/);

Jeśli pola oddzielone są dowolną liczbą dwukropków wówczs możemy użyć kodów WR. Program


$_ = "Capes:Goeff::Shot putter:::Big Avenue";
@personal = split(/:+/);
działa tak samo jak
  @personal =  ("Capes", "Goeff", "Shot putter", "Big Avenue");
natomiast
  $_ = "Capes:Goeff::Shot putter:::Big Avenue";
  @personal = split(/:/);
daje to samo co
  @personal =  ("Capes", "Goeff", "", "Shot putter", "", "", "Big Avenue");

Słowo można podzielić na znaki, zdanie na słowa, a akapit na zdania.

  @znaki  = split(//, $slowo);
  @slowa  = split(/ /, $zdanie);
  @zdania = split(/\./, $akapit);
W pierwszym przypadku łańcuch zerowy dopasowywany jest między dwoma znakami i właśnie dlatego tablica @znaki zawiera znaki, jest to więc tablica o elementach, którymi są łańcuchy o długości 1 znaku.

Ćwiczenie

Użytecznym narzędziem w pracach z językami naturalnymi jest program współbrzmienia (concordance). Pozwala to na wypisanie konkretnego łańcucha wraz z kontekstem, tak jak występuje on w tekście. Przykładowo, program taki uruchomiony z łańcuchem docelowym the mógłby dać następujący wynik. Zwróć uwagę na to jak wyśrodkowany został łańcuch the.
  discovered (this is the truth) that when he
  t kinds of metal to the leg of a frog, an e
  rrent developed and the frog's leg kicked,
  longer attached to the frog, which was dea
  normous advances in the field of amphibian
  ch it hop back into the pond -- almost.  Bu
  ond -- almost.  But the greatest Electrical
  ectrical Pioneer of them all was Thomas Edi
Ćwiczenie polega na napisaniu takiego właśnie programu. Oto pewne wskazówki. Program w tej postaci byłby całkiem przyzwoity, lecz łańcuch docelowy nie będzie wyrównany w pionie. W celu wyrównania go potrzebna jest funkcja substr. Oto trzy przykłady jej zastosowania.
substr("Once upon a time", 3, 4);	# zwraca "e up"
substr("Once upon a time", 7);		# zwraca "on a time"
substr("Once upon a time", -6, 5);	# zwraca "a tim"
Przykład pierwszy zwraca podłańcuch o długości 4 poczynając od pozycji 3. Pamiętajmy, że pierwszy znak łańcucha ma numer 0. Drugi przykład pokazuje, że jeśli opuścimy długość to jest wybierana cała prawa część łańcucha. Trzeci przykład pokazuje, że można wybrać podłańcuch poczynając od końca łańcucha przez podanie ujemnego indeksu. Tutaj zwracany jest podłańcuch zaczynający się w miejscu 6, licząc od końca, i posiada on 5 elementów.

Jeśli użyjemy ujemnego indeksu, który wybierze podłańcuch rozciągający się przed początek łańcucha, perl ostrzeże nas lub nic nie zwróci. Aby ustrzec się takiej sytuacji można użyć wspomnianego wcześniej operatora x i uzupełnić łańcuch. Np. wyrażenie (" "x30) tworzy 30 odstępów.

Tablice asocjacyjne

Dostęp do zwykłych tablic listowych odbywa się przez podanie numeru elementu. Pierwszym elementem tablicy @owoce jest $owoce[0], drugim jest $owoce[1], i tak dalej. perl pozwala również tworzyć tablice, do których można odwoływać się podając łańcuch. Nazywamy je tablicami asocjacyjnymi.

Aby zdefiniować tablicę asocjacyjną używa się zwykłego zapisu z nawiasami, lecz sama nazwa tablicy poprzedzona jest znakiem %. Załóżmy, że tworzymy tablicę osób wraz z ich wiekiem. Można to zrobić następująco:


  %wiek = ("Michael Caine", 39,
           "Dirty Dean", 34,
           "Angie", 27,
           "Willy", "21 in dog years",
           "The Queen Mother", 108);

Wiek osób możemy znaleźć używając następujących wyrażeń


  $wiek{"Michael Caine"};      # zwraca 39
  $wiek{"Dirty Dean"};         # zwraca 34
  $wiek{"Angie"};              # zwraca 27
  $wiek{"Willy"};              # zwraca "21 in dog years"
  $wiek{"The Queen Mother"};   # zwraca 108
Zauważmy, że podobnie jak w przypadku tablic listowych, aby otrzymać konkretny element, każdy znak % został zastąpiony przez znak $ ponieważ element tablicy jest skalarem. Inaczej niż w przypadku zwykłych tablic, indeks (w tym przypadku imię osoby) zawarty jest w nawiasach sześciennych, co świadczy o subtelnośći tablic asocjacyjnych w porównaniu ze zwykłymi tablicami.

Tablice asocjacyjne można zamienić na zwykłe tablice listowe przez przypisanie ich do zmiennej talicy typu listy. Zwykłe tablice można z kolei przywrócić do postaci tablic asocjacyjnych przez przypisanie ich do zmiennej typu tablicy asocjacyjnej. Zwykłe tablice muszą mieć parzystą liczbę elementów:


@info = %wiek;        # @info jest listą. Posiada
                      # aktualnie 10 elementów
$info[5];             # zwraca wartość 27
                      # z listy (tablicy) @info
%jeszczewiek = @info; # %jeszczewiek jest tablicą asocjacyjną
                      # jest ona identyczna jak %wiek

Operatory

Tablice asocjacyjne nie mają ustalonego porządku elementów (podobnie jak tzw. hasze) można jednakże otrzymać ich wszystkie elementy w kolejności używając funkcji keys oraz funkcji values:

  foreach $osoba (keys %wiek)
  {
        print "Znam wiek $osoba\n";
  }
  foreach $lata (values %wiek)
  {
        print "Kto ma $lata lat?\n";
  }
Kiedy wywołujemy funkcję keys wówczas zwraca ona listę kluczy (indeksów) tablicy asocjacyjnej. Kiedy używana jest funkcja values zwracana jest lista wartości tablicy. Obie funkcje zwracają odpowiednie listy w ustalonym porządku, lecz nie ma on nic wspólnego z porządkiem, w którym wprowadza się kolejne elementy.

Kiedy funkcja keys/values wywoływana jest w kontekście skalarnym, zwracana jest liczba równa liczbie par klucz/wartość w tablicy asocjacyjnej.

Istnieje też funkcja each, która zwraca listę dwuelementową: klucz i jego wartość. Za każdym wywołaniem zwracana jest inna para klucz/wartość:

while (($osoba, $lata) = each($wiek))
{
      print "$osoba ma $lata lat\n";
}

Zmienne środowiskowe

Kiedy uruchamiamy program perla, lub inny skrypt UNIXa, ustawiane są różne zmienne środowiskowe. Są to takie zmienne jak USER, zawierająca identyfikator użytkownika, zmienna DISPLAY, która informuje na który ekran wyświetlić grafikę. Kiedy uruchamiamy skrypt CGI perla, wówczas ustawiane są dodatkowe zmienne środowiskowe, które zawierają inne użyteczne informacje. Wszystkie zmienne środowiskowe i ich wartości umieszczane są w asocjacyjnej tablicy zwanej %ENV, której klucze (keys) są nazwami tych zmiennych. Spróbuj uruchomić następujący program perla:
  print "Nazywasz sie $ENV{'USER'} i pracujesz
  print "z ekranem $ENV{'DISPLAY'}\n;

Procedury

W perlu, podobnie jak w innych dobrych językach programowania, można definiować własne funkcje zwane procedurami (subroutines). Można je umieszczać w dowolnym miejscu programu. Dobrze jest jednak umieścić je albo na początku programu, albo przy końcu. Procedura wygląda następująco

  sub mojaprocedura
  {
      print "Jest to niezbyt ciekawa procedura\n";
      print "Robi zawsze to samo\n";
  }
niezależnie od dowolnych parametrów, które chcemy jej przekazać. Poniższe polecenia powodują, że procedura zadziała. Zauważ, że procedura wywoływana jest ze znakiem & przed nazwą.
  &mojaprocedura;         # wywołanie procedury
  &mojaprocedura($_)      # wywołanie z parametrem
  &mojaprocedura(1+2, $_  # wywołanie z dwoma parametrami

Parametry

W powyższych wywołaniach parametry są akceptowane, lecz są ignorowane. Kiedy wywołujemy procedurę jej parametry przekazywane są w postaci listy w specjalnej tablicy listowej @_. Zmienna ta nie ma absolutnie nic wspólnego ze zmienną skalarną $_. Następna procedura drukuje listę parametrów, z którymi została wywołana. Pokazano następnie kilka przykładów jej użycia.
  sub printargs
  {
      print "@_\n";
  }
  &printargs("perly", "king");        # drukuje "perly king"
  &printargs("frog", "and", "toad");  # drukuje "frog and toad"
Podobnie jak w przypadku innych tablic, elementy tablicy @_ są dostępne poprzez użycie nawiasów kwadratowych:
  sub printdwapierwsze
  {
      print "Pierwszy argument jest $_[0]\n";
      print "a $_[1] jest drugim argumentem\n";
  }
Znów należy podkreślić, że indeksowane skalary $_[0] i $_[1] nie mają nic wspólnego ze skalarem $_, którego możemy nadal używać bez obawy kolizji.

Zwracanie wartości

Wynikiem działania procedury jest zawsze ostatnio wykonana operacja. Poniższa procedura zwraca większy z dwu podanych parametrów. Po definicji procedury podano przykłady jej użycia.
sub maksymalny
{
      if ($_[0] > $_[1])
      {
             $_[0];
      }
      else
      {
             $_[1];
      }
}

$wiekszy = &maksymalny(37, 24)      # $wiekszy jest równy 37

Procedura &printdwapierwsze też zwraca wartość. Jest to 1. Jest tak dlatego, że ostatnią wykonaną rzeczą jest polecenie print, a wynikiem wykonania print jest zawsze 1.

Zmienne lokalne

Zmienna @_ jest lokalna w danej procedurze. Podobnie, lokalnymi zmiennymi są $_[0], $_[1], $_[2], etc. Inne zmienne można również uczynić lokalnymi. Jest to wygodne wtedy, gdy chcemy zmieniać parametry wejściowe. Poniższa procedura sprawdza czy jeden z łańcuchów znajduje się wewnątrz drugiego pomijając spacje. Dalej następują przykłady.
sub inside
{
      local($a, $b);              # utwórz zmienne lokalne
      ($a, $b) = ($_[0], $_[1]);  # dokonaj przypisania wartości
      $a =~ s/ //g;               # usuń spacje
      $b =~ s/ //g;               #  w zmiennych lokalnych
      ($a =~ /$b/ || $b =~ /$a/   # czy $a zawiera się w $b
                                  # czy też może na odwrót
}
&inside("lemon", "dole money");   # prawda
Procedurę można uprościć zapisując dwie pierwsze linie w postaci
local($a, $b) = ($_[0], $_[1]);

Last modified: pią mar 2017-03-24 16:08:26 by andrzejbaran