Home Dokumentacje Efektywne programowanie w AWK - Podręcznik użytkownika GNU awk - Wyrażenia regularne
19 | 08 | 2019
Efektywne programowanie w AWK - Podręcznik użytkownika GNU awk - Wyrażenia regularne Drukuj

Przejdź do pierwszej, poprzedniej, następnej, ostatniej sekcji, spisu treści.

 


 

4. Wyrażenia regularne

Wyrażenie regularne (regular expression), lub regexp, jest metodą opisu zbioru łańcuchów. Ponieważ wyrażenia regularne są tak podstawową częścią programowania w języku awk, ich format i stosowanie wymagają odrębnego rozdziału.

Wyrażenie regularne ujęte w ukośniki (`/') jest wzorcem awk, dopasowującym każdy rekord wejściowy, którego tekst należy do tego zbioru.

Najprostszym wyrażeniem regularnym jest ciąg liter, cyfr lub zarówno liter jak cyfr. Takie wyrażenie regularne dopasowuje dowolny łańcuch zawierający ten ciąg. Zatem, wyrażenie regularne `foo' dopasowuje dowolny łańcuch zawierający `foo'. Stąd też, wzorzec /foo/ dopasowuje dowolny rekord wejściowy zawierający trzy znaki `foo', w dowolnym miejscu rekordu. Inne rodzaje wyrażeń regularnych pozwalają na określanie bardziej skomplikowanych klas łańcuchów.

Początkowo, przykłady będą proste. Kiedy wyjaśnimy więcej na temat działania wyrażeń regularnych, przedstawimy bardziej skomplikowane

4.1. Jak stosować wyrażenia regularne

Przez umieszczenie go w ukośnikach wyrażenie regularne może być używane jako wzorzec. Jest ono wówczas sprawdzane z całym tekstem każdego rekordu. (Normalnie, wystarczy mu dopasowanie tylko części tekstu, by sprawdzenie się powiodło.) To, na przykład, wypisuje drugie pole z każdego rekordu, który gdziekolwiek zawiera trzy znaki `foo':

$ awk '/foo/ { print $2 }' BBS-list
-| 555-1234
-| 555-6699
-| 555-6480
-| 555-2127

Wyrażeń regularnych można też używać w wyrażeniach dopasowania. Wyrażenia takie umożliwiają podanie łańcucha, który ma być dopasowywany; nie musi to być cały bieżący rekord wejściowy. Porównań wyrażeń regularnych dokonują dwa operatory, `~' i `!~'. Wyrażenia wykorzystujące te operatory mogą być używane jako wzorce lub w instrukcjach if, while, for i do.

wyr ~ /regexp/
Jest prawdziwe jeśli wyrażenie wyr (wzięte jako łańcuch) pasuje do regexp. Poniższy przykład dopasowuje, lub wybiera, wszystkie rekordy wejściowe zawierające dużą literę `J' gdzieś w pierwszym polu:
$ awk '$1 ~ /J/' inventory-shipped
-| Jan  13  25  15 115
-| Jun  31  42  75 492
-| Jul  24  34  67 436
-| Jan  21  36  64 620
To samo robi to:
awk '{ if ($1 ~ /J/) print }' inventory-shipped
wyr !~ /regexp/
Jest prawdą jeśli wyrażenie wyr (wzięte jako łańcuch znakowy) nie pasuje do regexp. Poniższy przykład dopasowuje, lub wybiera, wszystkie rekordy wejściowe, których pierwsze pole nie zawiera dużej litery `J':
$ awk '$1 !~ /J/' inventory-shipped
-| Feb  15  32  24 226
-| Mar  15  24  34 228
-| Apr  31  52  63 420
-| May  16  34  29 208
...

Gdy wyrażenie regularne zapisane jest w ukośnikach, jak /foo/, nazywamy je stałą regexp (wyrażeniem regularnym stałym), podobnie jak 5.27 jest stałą liczbową, a "foo" jest stałą łańcuchową.

4.2. Sekwencje specjalne

Pewne znaki nie mogą być zawarte dosłownie w stałych łańcuchowych ("foo") czy stałych wyrażeniach regularnych (/foo/). Zamiast tego są reprezentowane przez sekwencje specjalne (escape sequences), będące ciągami znaków rozpoczynającymi się od odwrotnego ukośnika (`\').

Jednym z zastosowań sekwencji specjalnych jest włączanie znaku cudzysłowu do stałej łańcuchowej. Ponieważ zwykły cudzysłów kończyłby łańcuch, musimy użyć `\"' do przedstawienia rzeczywistego znaku cudzysłowu jako części łańcucha. Na przykład:

$ awk 'BEGIN { print "He said \"hi!\" to her." }'
-| He said "hi!" to her.

Sam znak odwrotnego ukośnika jest innym znakiem, który nie może dołączany normalnie; piszemy `\\' by umieścić pojedynczy odwrotny ukośnik w łańcuchu lub wyrażeniu regularnym. Zatem, łańcuch, którego zawartością są dwa znaki: `"' i `\', musi zostać zapisany "\"\\".

Innym zastosowaniem odwrotnego ukośnika jest reprezentacja znaków takich jak tabulacja czy znak nowej linii. Mimo, że nic nie powstrzymuje cię przed wprowadzeniem większości znaków niedrukowalnych do stałej łańcuchowej czy regexp, mogą one wyglądać paskudnie.

A oto tabela wszystkich sekwencji specjalnych używanych w awk, razem z tym, co oznaczają. Wszystkie te sekwencje mają zastosowanie zarówno do stałych łańcuchowych jak i do stałych regexp, chyba że powiedziano inaczej.

\\
Dosłowny odwrotny ukośnik (backslash), `\'.
\a
Znak alarmu (dzwonek, "alert"), Control-g, kod ASCII 7 (BEL).
\b
Backspace, Control-h, kod ASCII 8 (BS).
\f
Wysuw strony, formfeed, Control-l, kod ASCII 12 (FF).
\n
Nowa linia, newline, Control-j, kod ASCII 10 (LF).
\r
Powrót karetki, carriage return, Control-m, kod ASCII 13 (CR).
\t
Tabulacja pozioma, Control-i, kod ASCII 9 (HT).
\v
Tabulacja pionowa, Control-k, kod ASCII 11 (VT).
\nnn
Wartość ósemkowa nnn, gdzie nnn to jedna do trzech cyfr od `0' do `7'. Na przykład, kodem znaku ASCII ESC (escape) jest `\033'.
\xhh...
Wartość szesnastkowa hh, gdzie hh są cyframi szesnastkowymi (`0' do `9' i albo `A' do `F' albo `a' do `f'). Podobnie jak w takiej samej konstrukcji w C, sekwencja specjalna rozciąga się do napotkania pierwszego znaku nie będącego cyfrą szesnastkową. Jednak użycie więcej niż dwu cyfr szesnastkowych daje niezdefiniowane rezultaty. (Sekwencja specjalna `\x' nie jest dozwolona w POSIX-owym awk.)
\/
Dosłowny ukośnik (niezbędny tylko w stałych regexp). Sekwencji tej używa się chcąc zapisać stałą regexp zawierającą ukośnik. Ponieważ samo wyrażenie regularne jest ograniczone ukośnikami, należy zacytować ukośnik będący częścią wzorca, sygnalizując awk, by przetwarzał resztę wyrażenia.
\"
Dosłowny cudzysłów (niezbędny tylko w stałych łańcuchowych). Używany tylko gdy chcemy zapisać stałą łańcuchową zawierającą cudzysłów. Ponieważ łańcuch ograniczany jest cudzysłowami, należy zacytować ukośnik będący częścią łańcucha, sygnalizując awk, by przetwarzał resztę łańcucha.

W gawk istnieją dwie dodatkowe sekwencje specjalne, rozpoczynające się odwrotnym ukośnikiem i mające specjalne znaczenie w wyrażeniach regularnych. Zob. 4.4. Dodatkowe operatory regexp w gawk.

Co się stanie, jeśli w stałej łańcuchowej umieścimy odwrotny ukośnik przed czymś, co nie jest jednym ze znaków wyszczególnionych powyżej? POSIX awk celowo pozostawia ten przypadek niezdefiniowany. Są dwie możliwości.

  • Usunięcie odwrotnego ukośnika. Robi tak zarówno uniksowy awk, jak i gawk. Na przykład, "a\qc" jest tym samym co "aqc".
  • Pozostawienie odwrotnego ukośnika w spokoju. Robią tak niektóre inne implementacje awk. W takich implementacjach, "a\qc" jest tym samym, co napisanie "a\\qc".

W wyrażeniu regularnym, odwrotny ukośnik przed znakiem nie znajdującym się w powyższej tablicy, i nie wymienionym w 4.4. Dodatkowe operatory regexp w gawk, oznacza, że kolejny znak powinien być brany dosłownie, nawet jeśli normalnie byłby on operatorem wyrażeń regularnych. Np., /a\+b/ dopasowuje trzy znaki `a+b'.

Dla zapewnienia pełnej przenośności, nie stosuj odwrotnego ukośnika przed żadnym ze znaków nie podanych w tablicy powyżej.

Nasuwa się inna interesująca kwestia. Załóżmy, że używamy sekwencji specjalnej z wartością ósemkową lub szesnastkową do reprezentacji metaznaku wyrażeń regularnych (zob. 4.3. Operatory wyrażeń regularnych). Czy awk potraktuje ten znak jako znak dosłowny, czy też jako operator wyrażeń regularnych?

Okazuje się, że historycznie takie znaki były brane dosłownie (c.k.). Jednak standard POSIX wskazuje, że powinny być one traktowane jak rzeczywiste metaznaki, i tak to robi gawk. Jednak, w trybie zgodności (zob. 14.1. Opcje wiersza poleceń), gawk traktuje znaki reprezentowane przez ósemkowe lub szesnastkowe sekwencje specjalne dosłownie, gdy są one stosowane w stałych regexp. Zatem, /a\52b/ jest równoważne /a\*b/.

Podsumowując:

  1. Sekwencje specjalne z tablicy powyżej są zawsze przetwarzane jako pierwsze, zarówno w stałych łańcuchowych jak i w stałych regexp. Dzieje się to bardzo wcześnie, zaraz po odczycie twego programu przez awk.
  2. gawk przetwarza zarówno stałe wyrażenia regularne jak i dynamiczne wyrażenia regularne (zob. 4.7. Stosowanie dynamicznych wyrażeń regularnych), specjalne operatory podano w 4.4. Dodatkowe operatory regexp w gawk.
  3. Odwrotny ukośnik przed każdym innym znakiem oznacza, że znak ten należy traktować dosłownie.

4.3. Operatory wyrażeń regularnych

Wyrażenia regularne można łączyć za pomocą niżej opisanych znaków, zwanych operatorami wyrażeń regularnych, lub metaznakami, zwiększając moc i wszechstronność wyrażeń regularnych.

Wewnątrz wyrażeń regularnych poprawne są sekwencje specjalne opisane powyżej w 4.2. Sekwencje specjalne. Są one wprowadzane przez `\'. Ich rozpoznawanie i przekształcanie na odpowiadające im rzeczywiste znaki jest pierwszym krokiem przetwarzania wyrażeń regularnych.

A oto tabela metaznaków. Wszystkie znaki nie będące sekwencjami specjalnymi i nie podane w tej tabeli znaczą same siebie.

\
Służy do wyeliminowania specjalnego znaczenia znaku podczas dopasowywania. Na przykład:
\$
dopasowuje znak `$'.
^
Dopasowuje początek łańcucha. Na przykład:
^@chapter
dopasowuje `@chapter' na początku łańcucha, i może być wykorzystane do identyfikacji rozdziałów w plikach źródłowych Texinfo. `^' znany jest jako kotwica, gdyż zaczepia wzorzec tak, by pasował tylko na początku łańcucha. Należy zdawać sobie sprawę z tego, że `^' nie dopasowuje początku wiersza zawartego w łańcuchu. W tym przykładzie warunek nie jest prawdziwy:
if ("wiersz1\nWIERSZ 2" ~ /^W/) ...
$
Podobne do `^', ale pasuje tylko na końcu łańcucha. Na przykład:
p$
dopasowuje rekord kończący się na `p'. `$' jest również kotwicą, i również nie dopasowuje końca wiersza zawartego wewnątrz łańcucha. W tym przykładzie warunek nie jest prawdziwy:
if ("wiersz1\nWIERSZ 2" ~ /1$/) ...
.
Kropka dopasowuje dowolny pojedynczy znak, również znak nowej linii. Na przykład:
.P
dopasowuje w łańcuchu dowolny pojedynczy znak, po którym następuje `P'. Posługując się konkatenacją (łączeniem) można tworzyć takie wyrażenia regularne jak np. `U.A', dopasowujące dowolną trzyznakową sekwencję rozpoczynającą się od `U' a kończącą na `A'. W trybie ścisłej zgodności z POSIX (zob. 14.1. Opcje wiersza poleceń), `.' nie dopasowuje znaku NUL, będącego znakiem o wszystkich bitach równych zero. Bez ścisłej zgodności NUL jest po prostu znakiem jak każdy inny. Inne wersje awk mogą nie umieć dopasować znaku NUL.
[...]
Zwane listą znaków. Dopasowuje dowolny jeden znak spośród znaków zawartych w nawiasach kwadratowych. Na przykład:
[MVX]
dopasowuje w łańcuchu dowolny ze znaków `M', `V' albo `X'. Zakresy znaków wskazywane są przez użycie myślnika pomiędzy znakiem rozpoczynającym a kończącym zakres, i ujęcie całości w nawiasy kwadratowe. Na przykład:
[0-9]
dopasowuje dowolną cyfrę. Dopuszcza się wielokrotne zakresy. Np. lista [A-Za-z0-9] jest popularnym sposobem wyrażania pojęcia "wszystkich znaków alfanumerycznych" [tłum.: w alfabecie łacińskim]. Chcąc w liście ująć jeden ze znaków `\', `]', `-' lub `^' należy postawić przed nim `\'. Na przykład:
[d\]]
dopasowuje albo `d' albo `]'. Takie traktowanie odwrotnego ukośnika `\' w liście znaków jest zgodne z innymi implementacjami awk, jest też wymagane przez POSIX. Wyrażenia regularne w awk są nadzbiorem specyfikacji POSIX opisującej rozszerzone wyrażenia regularne (Extended Regular Expressions, EREs). ERE POSIX-a oparte są na wyrażeniach regularnych akceptowanych przez tradycyjne narzędzie egrep. Klasy znaków są nową cechą wprowadzoną w standardzie POSIX. Klasa znaków jest specjalną notacją opisującą listy znaków mających specyficzne właściwości, gdzie jednak same konkretne znaki mogą zmieniać się w zależności od kraju i/lub zestawu znaków. Na przykład, pojęcie tego, co jest znakiem alfabetu, jest różne w USA i we Francji. Klasa znaków jest poprawna wyłącznie wewnątrz nawiasów kwadratowych listy znaków w wyrażeniu regularnym. Klasy znaków składają się z `[:', słowa kluczowego oznaczającego klasę, i `:]'. Oto klasy znaków zdefiniowane przez standard POSIX.
[:alnum:]
Znaki alfanumeryczne.
[:alpha:]
Znaki alfabetyczne.
[:blank:]
Znaki spacji i tabulacji.
[:cntrl:]
Znaki sterujące.
[:digit:]
Znaki numeryczne.
[:graph:]
Znaki, które są równocześnie drukowalne i widoczne. (Spacja jest drukowalna, lecz nie jest widoczna, podczas gdy `a' jest drukowalne i widoczne.)
[:lower:]
Znaki małych liter alfabetu.
[:print:]
Znaki drukowalne (znaki nie będące znakami sterującymi).
[:punct:]
Znaki przestankowe (znaki nie będące literami, cyframi, znakami sterującymi czy odstępami).
[:space:]
Znaki odstępu (jak spacja, tabulacja czy wysuw strony, by wymienić tylko niektóre z nich).
[:upper:]
Znaki dużych liter alfabetu.
[:xdigit:]
Znaki będące cyframi szesnastkowymi.
Na przykład, przed standardem POSIX, by dopasować znaki alfanumeryczne, należało napisać /[A-Za-z0-9]/. Jeżeli wykorzystywany zestaw znaków miał inne znaki alfabetu, to powyższe by ich nie dopasowywało. Za pomocą POSIX-owych klas znaków można napisać /[[:alnum:]]/, i będzie to dopasowywać wszystkie znaki literowe i cyfrowe naszego zestawu znaków. W listach znaków mogą pojawić się dwie dodatkowe specjalne sekwencje. Mają one zastosowanie w zestawach znaków innych niż ASCII, mających pojedyncze symbole (zwane elementami porządkowania, collating elements) reprezentowane przez więcej niż jeden znak, lub też kilka znaków, które są sobie równoważne przy porządkowaniu czy sortowaniu. (Np. we francuskim zwykłe "e" i z akcentem grave "`e" są równoważne.)
Symbole porządkowania (collating symbols)
Symbol porządkowania jest wieloznakowym elementem porządkowania ujętym pomiędzy `[.' a `.]'. Na przykład, jeśli `ch' jest elementem porządkowania, to [[.ch.]] jest wyrażeniem regularnym dopasowującym ten element porządkowania, podczas gdy [ch] jest wyrażeniem regularnym dopasowującym albo `c' albo `h'.
Klasy równoważności (equivalence classes)
Klasa równoważności jest specyficzną dla ustawień narodowych (locale) nazwą znaków, które są sobie równoważne. Nazwa ta jest ujęta między `[=' i `=]'. Na przykład, można użyć nazwy `e' do przedstawienia wszystkich "e", "`e" i "'e". W tym przypadku, [[=e]] jest wyrażeniem regularnym dopasowującym dowolny ze znaków `e', `'e' lub ``e'.
Możliwości te są bardzo cenne przy ustawieniach innych niż język angielski. Uwaga! Funkcje biblioteczne wykorzystywane przez gawk do dopasowywania wyrażeń regularnych rozpoznają obecnie jedynie POSIX-owe klasy znaków; nie rozpoznają symboli porządkowania ani klas równoważności.
[^ ...]
Jest to dopełniająca lista znaków. Pierwszym znakiem po `[' musi być `^'. Zapis ten dopasowuje dowolne znaki oprócz tych, które znajdują się w nawiasach kwadratowych. Na przykład:
[^0-9]
dopasowuje dowolny znak nie będący cyfrą.
|
Jest to operator alternatywy. Służy do podawania alternatyw. Na przykład:
^P|[0-9]
dopasowuje dowolny łańcuch, który pasuje albo do `^P' albo do `[0-9]'. To znaczy, że dopasowuje dowolny łańcuch zaczynający się od `P' lub zawierający cyfrę. Alternatywa odnosi się do największego możliwego wyrażenia regularnego występującego po jednej ze stron. Innymi słowy, `|' ma najniższy priorytet ze wszystkich operatorów wyrażeń regularnych.
(...)
Nawiasy zwykłe służą do grupowania wyrażeń regularnych i arytmetycznych. Można stosować je do sklejania wyrażeń regularnych zawierających operator alternatywy, `|'. Na przykład, `@(samp|code)\{[^}]+\}' dopasowuje zarówno `@code{foo}' jak i `@samp{bar}'. (Są to sekwencje sterujące formatowaniem Texinfo.)
*
Ten symbol oznacza, że poprzedzające go wyrażenie regularne ma być powtórzone tyle razy, ile jest to niezbędne do znalezienia dopasowania. Na przykład:
ph*
stosuje symbol `*' do poprzedzającego go `h' i szuka dopasowań pojedynczego `p', po którym występuje dowolna ilość `h'. Dopasuje ono również samo `p', jeśli nie wystąpią żadne `h'. `*' powtarza najmniejsze możliwe poprzedzające go wyrażenie. (Chcąc powtórzyć większe wyrażenie należy użyć nawiasów.) Odnajduje tyle powtórzeń, ile to możliwe. Na przykład:
awk '/\(c[ad][ad]*r x\)/ { print }' sample
wypisuje każdy rekord z `sample' zawierający łańcuch postaci `(car x)', `(cdr x)', `(cadr x)', i tak dalej. Zauważ cytowanie nawiasów przez poprzedzenie ich odwrotnymi ukośnikami.
+
Symbol zbliżony do `*', ale poprzedzające go wyrażenie musi zostać dopasowane przynajmniej jeden raz. To znaczy, że:
wh+y
będzie dopasowywać `why' i `whhy', ale nie `wy', podczas gdy `wh*y' dopasowywałoby wszystkie te trzy łańcuchy. Prostszym sposobem zapisania ostatniego przykładu `*' jest:
awk '/\(c[ad]+r x\)/ { print }' sample
?
Symbol podobny do `*', ale poprzedzające wyrażenie regularne może być dopasowane raz albo wcale. Na przykład:
fe?d
dopasuje `fed' i `fd', ale nic innego.
{n}
{n,}
{n,m}
Jedna lub dwie liczby w nawiasach klamrowych oznaczają wyrażenie przedziałowe (interval expression). Jeśli w nawiasach jest jedna liczba, to poprzedzające wyrażenie regularne powtarzane jest n razy. Jeżeli są tam dwie liczby rozdzielone przecinkiem, to poprzedzające wyrażenie powtarzane jest od n do m razy. Jeśli jest tam jedna liczba, po której następuje przecinek, to poprzedzające wyrażenie powtarzane jest co najmniej n razy.
wh{3}y
dopasowuje `whhhy', ale nie `why' czy `whhhhy'.
wh{3,5}y
dopasowuje tylko `whhhy' lub `whhhhy' lub `whhhhhy'.
wh{2,}y
dopasowuje `whhy' lub `whhhy', i tak dalej.
Wyrażenia przedziałowe nie były tradycyjnie dostępne w awk. Zostały dodane jako część standardu POSIX, by awk i egrep były ze sobą zgodne. Ponieważ jednak stare programy mogą wykorzystywać `{' i `}' w stałych regexp, domyślnie gawk nie dopasowuje wyrażeń przedziałowych w wyrażeniach regularnych. Jeżeli podano opcję `--posix' lub `--re-interval' (zob. 14.1. Opcje wiersza poleceń), to wyrażenia przedziałowe w wyrażeniach regularnych są dozwolone.

W wyrażeniach regularnych najwyższy priorytet mają operatory `*', `+' id `?', tak samo jak nawiasy klamrowe `{' and `}'. Po nich występuje łączenie, i na koniec `|'. Tak jak w arytmetyce, nawiasy okrągłe mogą zmienić sposób grupowania operatorów.

Jeśli gawk pracuje w trybie zgodności (zob. 14.1. Opcje wiersza poleceń), klasy znaków i wyrażenia przedziałowe w wyrażeniach regularnych nie są dostępne.

W następnej sekcji omówiono operatory wyrażeń regularnych specyficzne dla GNU i podano więcej szczegółów dotyczących tego, jak opcje wiersza poleceń wpływają na sposób, w jaki gawk interpretuje znaki w wyrażeniach regularnych.

4.4. Dodatkowe operatory regexp w gawk

Programy GNU mające działające na wyrażeniach regularnych udostępniają kilka dodatkowych operatorów tych wyrażeń. Opisano je w niniejszej sekcji. Są one specyficzne dla gawk; nie są dostępne w innych implementacjach awk.

Większość dodatkowych operatorów przeznaczonych jest do zadań związanych z dopasowywaniem słów. Do naszych celów, słowo jest ciągiem jednej lub więcej liter, cyfr lub znaków podkreślenia (`_').

\w
Ten operator dopasowuje dowolny ze znaków tworzących słowa, tj. dowolną literę, cyfrę lub podkreślenie. Można traktować go jako skrót dla [[:alnum:]_].
\W
Dopasowuje dowolny znak nie będący znakiem współtworzącym słowa. Można traktować go jako skrót dla [^[:alnum:]_].
\<
Ten operator dopasowuje pusty łańcuch na początku słowa. Na przykład, /\<away/ dopasowuje `away', ale nie `stowaway'.
\>
Ten operator dopasowuje pusty łańcuch na końcu słowa. Na przykład, /stow\>/ dopasowuje `stow', ale nie `stowaway'.
\y
Ten operator dopasowuje pusty łańcuch albo na początku albo na końcu słowa. Na przykład, (angielskie boundary to "granica"). Na przykład, `\yballs?\y' dopasowuje `ball' lub `balls' jako osobne słowo.
\B
Dopasowuje pusty łańcuch wewnątrz słowa. Inaczej mówiąc, `\B' dopasowuje łańcuch pusty występujący pomiędzy dwoma znakami budującymi słowa. Na przykład, /\Brat\B/ dopasowuje `crate', ale nie dopasowuje `dirty rat'. `\B' jest w gruncie rzeczy przeciwieństwem `\y'.

Istnieją jeszcze dwa inne operatory, operujące na buforach. W Emacsie bufor jest, naturalnie, buforem Emacsa. Przy innych programach, procedury biblioteczne regexp używane przez gawk traktują jako bufor cały łańcuch jaki ma zostać dopasowany.

W awk, ponieważ `^' i `$' zawsze działają jako początek i koniec łańcuchów, wspomniane operatory nie wnoszą żadnych nowych możliwości. Udostępniane są z uwagi na zgodność z innym oprogramowaniem GNU.

\`
Operator ten dopasowuje pusty łańcuch na początku bufora.
\'
Operator ten dopasowuje pusty łańcuch na końcu bufora.

W innych programach GNU operatorem granicy słowa jest `\b'. Jednak powoduje to konflikt w występującą w języku awk definicji `\b' jako backspace, więc gawk używa innej litery.

Metodą alternatywną byłby wymóg dwu odwrotnych ukośników w operatorach GNU, ale zostało to uznane za zbyt mylące, a obecna metoda stosowania `\y' dla GNU `\b' wygląda na mniejsze zło.

Rozmaite opcje wiersza poleceń (zob. 14.1. Opcje wiersza poleceń) decydują o sposobie, w jaki gawk interpretuje znaki w wyrażeniach regularnych.

Bez opcji
W przypadku domyślnym udostępnia wszystkie możliwości wyrażeń regularnych POSIX-a i operatory GNU opisane powyżej. Wyrażenia przedziałowe nie są jednak obsługiwane.
--posix
Obsługiwane są tylko wyrażenia regularne POSIX, operatory GNU nie mają specjalnego znaczenia (np., `\w' dopasowuje dosłowne `w'). Wyrażenia przedziałowe są dozwolone.
--traditional
Dopasowywane są wyrażenia regularne tradycyjnego uniksowego awk. Operatory GNU nie mają znaczenia specjalnego, nie są dostępne wyrażenia przedziałowe ani POSIX-owe klasy znaków ([[:alnum:]] i tak dalej). Znaki opisane ósemkowymi i szesnastkowymi sekwencjami specjalnymi traktowane są dosłownie, nawet jeśli reprezentują metaznaki wyrażeń regularnych.
--re-interval
Dopuszcza wyrażenia przedziałowe w wyrażeniach regularnych, nawet jeśli podano `--traditional'.

4.5. Uwzględnianie wielkości liter w dopasowaniach

Normalnie w wyrażeniach regularnych wielkość liter jest znacząca, zarówno przy dopasowywaniu zwykłych znaków (tj. nie metaznaków), jak i wewnątrz zestawów znaków. Stąd `w' w wyrażeniu regularnych dopasowuje wyłącznie małą literę `w' a nie duże `W'.

Najprostszym sposobem wykonania dopasowania niezależnego od wielkości liter jest użycie listy znaków: `[Ww]'. Może być on jednak niewygodny jeśli trzeba stosować go często; utrudnia też czytanie wyrażeń regularnych. Istnieją dwie alternatywne metody, które mogą się bardziej podobać.

Jednym ze sposobów wykonania dopasowania bez rozróżniania wielkości liter w danym miejscu programu jest konwersja danych na jednakową wielkość liter, za pomocą wbudowanych funkcji łańcuchowych tolower lub toupper (których jeszcze nie omawialiśmy; zob. 12.3. Funkcje wbudowane działające na łańcuchach). Na przykład:

tolower($1) ~ /foo/  { ... }

przekształca pierwsze pole na małe litery przed wykonaniem na nim dopasowania. Działa to w dowolnej implementacji awk zgodnej z POSIX.

Inną metodą, specyficzną dla gawk, jest nadanie zmiennej IGNORECASE wartości niezerowej (zob. 10. Zmienne wbudowane). Gdy IGNORECASE jest niezerowe, wszystkie operacje na wyrażeniach regularnych i łańcuchach ignorują wielkość liter. Zmiana wartości IGNORECASE steruje dynamicznie wrażliwością programu na wielkość liter w trakcie jego działania. Domyślnie wielkość znaków jest istotna, ponieważ IGNORECASE (jak większość zmiennych) jest inicjowana na zero.

x = "aB"
if (x ~ /ab/) ...   # ten test się nie powiedzie

IGNORECASE = 1
if (x ~ /ab/) ...   # a teraz się uda

Ogólnie, nie można wykorzystać IGNORECASE do zrobienia pewnych reguł niewrażliwymi na wielkość liter a innych wrażliwymi, gdyż nie ma możliwości ustawienia IGNORECASE tylko dla wzorca konkretnej reguły. Chcąc zrobić coś takiego, musimy użyć list znaków lub tolower. Jednak jedną z rzeczy, jakie można zrobić z IGNORECASE, jest dynamiczne włączanie lub wyłączanie uwzględniania wielkości liter dla wszystkich reguł naraz.

IGNORECASE można ustawić w wierszu poleceń lub w regule BEGIN (zob. 14.2. Inne argumenty wiersza poleceń; także zob. 8.1.5.1. Akcje początkowe i końcowe). Ustawienie IGNORECASE z wiersza poleceń jest sposobem na uczynienie programu niewrażliwym na wielkość liter bez konieczności zmieniania go.

Przed wersją 3.0 gawk, wartość IGNORECASE wpływała tylko na operacje na wyrażeniach regularnych. Nie dotyczyła porównywania łańcuchów przez `==', `!=', i tak dalej. Począwszy od wersji 3.0, IGNORECASE ma wpływ zarówno na działania na wyrażeniach regularnych jak i na łańcuchach.

Począwszy od wersji 3.0 gawk, równoważniki między dużymi a małymi znakami oparte są na zestawie znaków ISO-8859-1 (ISO Latin-1). Zestaw ten jest nadzbiorem tradycyjnych 128 znaków ASCII, udostępniającym też wiele znaków odpowiednich do użytku w językach europejskich [tłum.: niestety -- zachodnioeuropejskich].

Wartość IGNORECASE nie ma znaczenia jeśli gawk pracuje w trybie zgodności (zob. 14.1. Opcje wiersza poleceń). W trybie zgodności wielkość liter jest zawsze istotna.

4.6. Jak bardzo pasuje tekst?

Rozważmy następujący przykład:

echo aaaabcd | awk '{ sub(/a+/, "<A>"); print }'

Przykład ten wykorzystuje funkcję sub (której jeszcze nie omawialiśmy, zob. 12.3. Funkcje wbudowane działające na łańcuchach) do zmiany rekordu wejściowego. Występujące tu wyrażenie regularne /a+/ oznacza "co najmniej jeden znak `a'", a tekstem zastępującym jest `<A>'.

Wejście zawiera cztery znaki `a'. Jakie będzie wyjście? Inaczej mówiąc, ile to jest "co najmniej jeden" -- czy awk dopasuje dwa, trzy czy wszystkie cztery znaki `a'?

Odpowiedź brzmi: wyrażenia regularne awk (i POSIX-a) zawsze dopasowują pierwszy z lewej najdłuższy ciąg znaków wejściowych, jaki można dopasować. Zatem, w tym przykładzie, przez `<A>' zostaną zastąpione wszystkie cztery znaki `a'.

$ echo aaaabcd | awk '{ sub(/a+/, "<A>"); print }'
-| <A>bcd

Przy prostych sprawdzeniach typu pasuje/nie pasuje, nie jest to takie ważne. Jednak przy dopasowywaniu tekstów i podstawieniach przez funkcje match, sub, gsub i gensub, jest bardzo istotne. Zrozumienie tej zasady jest również istotne przy podziale na pola i rekordy opartym na wyrażeniach regularnych (zob. 5.1. Jak wejście dzielone jest na rekordy, a także zob. 5.5. Jak rozdzielać pola).

4.7. Stosowanie dynamicznych wyrażeń regularnych

Prawa strona operatora `~' lub `!~' nie musi być stałym wyrażeniem regularnym (tj. łańcuchem znaków pomiędzy ukośnikami). Może być dowolnym wyrażeniem. Wyrażenie podlega wyliczeniu wartości i jej przekształceniu na łańcuch, jeśli zachodzi taka potrzeba; zawartość łańcucha używana jest jako wyrażenie regularne. Wyrażenie regularne obliczane w ten sposób nazywane jest dynamicznym wyrażeniem regularnym. Na przykład:

BEGIN { identifier_regexp = "[A-Za-z_][A-Za-z_0-9]*" }
$0 ~ identifier_regexp    { print }

przypisuje do identifier_regexp wyrażenie regularne opisujące nazwy zmiennych awk i sprawdza, czy rekord wejściowy pasuje do tego wyrażenia.

Uwaga! W stosowaniu operatorów `~' i `!~' istnieje różnica pomiędzy stałym regexp ujętym w ukośniki a stałą łańcuchową ujętą w cudzysłowy. Jeśli mamy zamiar użyć stałej łańcuchowej, powinniśmy rozumieć, że łańcuch w gruncie rzeczy badany jest dwukrotnie: za pierwszym razem gdy awk czyta program, i za drugim gdy zamierza dopasować łańcuch stojący po lewej stronie operatora ze wzorcem po prawej. Obowiązuje to nie tylko dla stałych łańcuchowych, ale i dowolnych wyrażeń o wartości łańcuchowej (jak identifier_regexp powyżej).

Co wynika z tego, że łańcuch jest sprawdzany dwukrotnie? Istotna różnica występuje przy sekwencjach specjalnych, w szczególności przy odwrotnych ukośnikach. W celu umieszczenia odwrotnego ukośnika w wyrażeniu regularnym wewnątrz łańcucha musimy wpisać dwa odwrotne ukośniki.

Na przykład, /\*/ jest stałą regexp opisującą dosłowne `*'. Potrzebny jest tylko jeden odwrotny ukośnik. Chcąc zrobić to samo za pomocą łańcucha, musielibyśmy wpisać "\\*". Pierwszy odwrotny ukośnik służy tylko do zacytowania drugiego, tak że łańcuch faktycznie zawiera dwa znaki `\' i `*'.

Skoro do opisu wyrażenia regularnego możemy użyć zarówno stałej typu regexp jak i stałej łańcuchowej, którą z nich powinniśmy zastosować? Odpowiedź brzmi: "stałą regexp", z kilku powodów.

  1. Stałe łańcuchowe są bardziej skomplikowane w pisaniu i trudniejsze w czytaniu. Stosowanie stałych regexp powoduje, że program staje się mniej podatny na błędy. Brak zrozumienia różnicy pomiędzy tymi dwoma rodzajami stałych jest typowym źródłem błędów.
  2. Używanie stałych regexp jest też bardziej efektywne: awk potrafi zauważyć, że podaliśmy wyrażenie regularne i przechowuje je wewnętrznie w postaci powodującej efektywniejsze dopasowywanie wzorców. Przy zastosowaniu stałej łańcuchowej, awk musi najpierw przekształcić łańcuch na taką postać wewnętrzną, a następnie dopiero wykonać dopasowanie wzorca.
  3. Używanie stałych regexp to lepszy styl pisania; jasno pokazuje, że mamy na myśli dopasowanie wyrażenia regularnego.

 


Przejdź do pierwszej, poprzedniej, następnej, ostatniej sekcji, spisu treści.

 
Linki sponsorowane

W celu realizacji usług i funkcji na witrynach internetowych ZUI "ELPRO" stosujemy pliki cookies. Korzystanie z witryny bez zmiany ustawień dotyczących plików cookies oznacza, że będą one zapisywane w urządzeniu wyświetlającym stronę internetową. Więcej szczegółów w Polityce plików cookies.

Akceptuję pliki cookies z tej witryny.