Hlavní navigace

Makro procesor GNU m4 (7)

Michal Burda 5. 11. 2001

Tato kapitola se zabývá vestavěnými makry, která umožňují různé manipulace s řetězci -- zjištěním délky řetězce počínaje a nahrazováním pomocí regulárních výrazů konče.

Makro procesor m4 poskytuje podporu pro základní manipulaci s řetězci. Všechna makra kromě příkazu format jsou rozpoznávána pouze s argumenty.

Zjištění délky řetězce

len(řetězec)

expanduje na délku předaného řetězce.

   len
=> len
   len()
=> 0
   len(`abcdef')
=> 6

Vyhledání podřetězce

Pozici podřetězce v řetězci vrací makro

index(řetězec, podřetězec)

První znak řetězce má index 0. Jestliže podřetězec není v řetězci nalezen, makro index expanduje na –1.

   index(`automobilismus', `mobil')
=> 4
   index(`Příležitostné makrování', `x')
=> -1

Získání podřetězce

Vrácení podřetězce daného řetězce dosáhnete pomocí makra substr:

substr(řetězec, start, délka)

substr expanduje na podřetězec daného řetězce, který bude začínat od indexu start a bude délka znaků dlouhý. Pokud zadaná délka přesáhne délku řetězce nebo ji vůbec nezadáte, vybere se podřetězec až do konce zdroje.

   substr(`bim bam bum', 4)
=> bam, bum
   substr(`bim bam bum', 4, 3)
=> bam

Makro pro přistupování k řetězci po znacích:

   define(`znak', `substr(`$1', `$2', 1)')
=>
   znak(`ahoj',0) znak(`ahoj',1)
=> a h

Nahrazování po znacích

Makro translit má podobnou funkčnost jako Unixová utilita tr.

translit(řetězec, sada vzorů, sada náhrad)

Jeho úkolem je nahradit všechny výskyty znaků ze sady vzorů odpovídajícími (podle pořadí) znaky ze sady náhrad. Jinými slovy, translit expanduje na řetězec, jehož každý znak, který se nalezne v sadě vzorů, bude nahrazen znakem ze sady náhrad se stejným indexem.

   define(`velka_pismena', `translit(`$1', `a-z', `A-Z')')

=> velka_pismena(`Ahoj Karle!')
   AHOJ KARLE!
=>

V příkladu jsme definovali makro, které převádí znaky svého argumentu na velká písmena. Makro bohužel trpí základní nemocí. Neumí převést znaky s českou diakritikou. Opravení tohoto nedostatku nechávám na pilném čtenáři.

Jestliže je sada vzorů delší než sada náhrad, odpovídající přebývající znaky budou ze zdrojového řetězce odstraněny. Pokud sadu náhrad neuvedete vůbec, budou z řetězce vymazány všechny znaky vyjmenované v sadě vzorů.

   define(`vymaz_samohlasky', `translit(`$1', `aeiouAEIOU')')
=>
   vymaz_samohlasky(`Ahoj Karle!')
=> hj Krl!

Jak jste si již asi všimli, znaky v sadách můžete zapsat pomocí intervalu, tj. například jako a-j, což znamená všechna malá písmena anglické abecedy od a do j. (Nebo 0–9, a to je stejné, jako byste napsali 0123456789.) Potřebujete-li do sady znaků zahrnout i pomlčku („-“), uveďte ji v seznamu na prvním nebo posledním místě. Je dovoleno dokonce psát i obrácené rozsahy. Třeba Z-A znamená všechna velká písmena anglické abecedy zapsaná od konce.

Jako ukázku si definujme jednoduchou dětskou šifru:

   define(`sifruj', `translit(`$1', `a-zA-Z0-9', `z-aZ-A9-0')')
=>
   sifruj(`Ahoj Karle!')
=> Zslq Pziov!
   sifruj(sifruj(`Ahoj Karle!'))
=> Ahoj Karle!

Formátování

V procesoru m4 naleznete i makro, které je velice podobné funkci printf z programovacího jazyka C.

format(formát, ...)

První argument je řetězec formátu, který může obsahovat speciální sekvence znaků začínající na %, a ty se určitým způsobem nahrazují hodnotami dalších parametrů makra. Makro format umí různě formátovat číselné hodnoty nebo řetězce. Podporuje všechny standardní sekvence printf: %c, %s, %d, %o, %x, %X, %u, %e, %E a %f. Akceptuje i specifikace délek položek, počtu desetinných míst a rozeznává modifikátory +, -, mezeru, 0, #, h a l. Na tomto místě nebudu popisovat, co která sekvence znamená, protože si myslím, že je to zbytečné, neboť funkci printf z céčka jistě většina z vás zná. (Pokud ne, můžete si o ní přečíst v kterékoliv knize o jazyku C nebo rovnou na manuálových stránkách (man 3 printf).)

Příklady:

   define(`makro', `Strč prst skrz krk')
=>
   format(`Řetězec "%s" je %d znaků dlouhý.', makro, len(makro))
=> Řetězec "Strč prst skrz krk" je 18 znaků dlouhý.

   define(`krat', `format(`%2d x %2d = %4d
   ', $1, $2, eval($1 * $2))')
=>
   krat(5,1)krat(5,5)krat(50,10)krat(50,50)
=>  5 x  1 =    5
    5 x  5 =   25
   50 x 10 =  500
   50 x 50 = 2500

Vyhledávání podle regulárního výrazu

Pro vyhledávání a nahrazování řetězců pomocí regulárního výrazu slouží makro

regexp(řetězec, reg-výraz [, náhrada])

Vynecháte-li argument náhrada, regexp expanduje na index prvního znaku podřetězce, který zadanému regulárnímu výrazu vyhoví. Pokud žádnou shodu nenalezne, vrátí –1.

   regexp(`Kapitola 1. - Úvod', `[0-9]+\. -')
=> 9
   regexp(`Ahoj světe!', `[0-9]+\. -')
=> -1

Makro regexp se všemi parametry expanduje na hodnotu třetího argumentu, přičemž všechny podřetězce tvaru \n, kde n je přirozené číslo, nahradí podřetězci, které odpovídají n-tému podvýrazu uzavřenému do závorek. \& se nahrazuje celým vyhovujícím podřetězcem.

   regexp(`bim bam bum',
          `\(\<\w+\) \(\<\w+\) \(\<\w+\)',
          `\3 \2 \1')
=> bum bam bim
   regexp(`Ahoj světe!', `.oj', `*** \& ***')
=> *** hoj ***

Příjemné je, že výsledný text znovu podléhá expanzi procesoru. Může tedy obsahovat volání dalších maker a docílíte tak opravdu divokých funkcí.

O samotných regulárních výrazech se už na Rootu psalo, proto je zde znovu vysvětlovat nebudu.

   define(`makro', `ahoj')
=>
   regexp(`macku', `..', `\&kro')
=> ahoj

Globální nahrazování podle regulárního výrazu

Pod tímto nechutným názvem se skrývá velice užitečné makro, jehož syntaxe je podobná regexpu:

patsubst(řetězec, reg-výraz[, náhrada])

patsubst prohledá zdrojový řetězec a místo každého podřetězce odpovídajícího regulárnímu výrazu vloží danou náhradu. Výsledkem expanze je tedy zdrojový řetězec s podřetězci vyhovujícími regulárnímu výrazu nahrazenými náhradou. To znamená, že části řetězce, které regulárnímu výrazu nevyhoví, jsou do expanze zahrnuty nezměněné. Kdykoliv nějaký podřetězec vyhoví danému regulárnímu výrazu, provede se náhrada a pokračuje se ve vyhledávání dalšího podřetězce za místem výskytu předchozí shody. Žádný podřetězec proto není nahrazován dvakrát. V parametru s náhradou můžete opět používat konstrukce \n a \& se stejným významem jako u makra regexp.

   define(`prostrkej', `patsubst(`$1', `.', `\& ')')
=>
   prostrkej(`Ahoj Karle!')
=> A h o j   K a r l e !

Třetí parametr může být vynechán. V takovém případě se každý regulárnímu výrazu vyhovující podřetězec vymaže.

   define(`vymaz_samohlasky2', `patsubst(`$1', `[aeiouAEIOU]')')
=>
   vymaz_samohlasky2(`Ahoj Karle!')
=> hj Krl!

Na závěr uvedu jeden realisticky vyhlížející příklad z manuálu k procesoru m4. Budeme definovat makro, které změní první písmena každého slova na velká a ostatní na malá:

   define(`velka_pismena', `translit(`$*', `a-z', `A-Z')')
=>
   define(`mala_pismena', `translit(`$*', `A-Z', `a-z')')
=>
   define(`$kapitalky',
          `regexp(`$1', `^\(\w\)\(\w*\)',
                  `velka_pismena(`\1')`'mala_pismena(`\2')')')
=>
   define(`kapitalky',
          `patsubst(`$1', `\w+', `indir(`$kapitalky', `\&')')')
=>
   kapitalky(`Ahoj KARLE! Jak se vede?')
=> Ahoj Karle! Jak Se Vede?

Aby makro kapitalky fungovalo spolehlivě i pro znaky s českou diakritikou, museli bychom upravit překladové sady maker velka_pismena a mala_pismena a také všechny regulární výrazy. Výraz \w totiž vyhoví jen písmenu anglické abecedy. Zkuste si to za domácí úkol opravit. :-)

Pokračování příště.

Našli jste v článku chybu?

6. 11. 2001 16:48

Michal Burda (neregistrovaný)

Trochu predbihate, ale dobre. Da se to udelat nekolika zpusoby:
pomoci makra dnl, ktere zpusobi to, ze m4 ignoruje vse az po novy radek (vcetne) nebo pomoci divert:

divert(-1)
sem muzete napsat cokoliv, nic z toho se nezapise na vystup...
takze treba:
define(`makro',`ahoj')
apod....
divert

co napisete zde uz na vystup poputuje...
(jeste existuje varianta pomoci ifdef, ale ta se mne osobne vubec nelibi :)












Podnikatel.cz: Babiše přesvědčila 89letá podnikatelka?!

Babiše přesvědčila 89letá podnikatelka?!

Měšec.cz: Kdy vám stát dá na stěhování 50 000 Kč?

Kdy vám stát dá na stěhování 50 000 Kč?

Vitalia.cz: Z tohoto konopí dělají léčivé masti

Z tohoto konopí dělají léčivé masti

DigiZone.cz: Recenze Westworld: zavraždit a...

Recenze Westworld: zavraždit a...

Podnikatel.cz: Přehledná titulka, průvodci, responzivita

Přehledná titulka, průvodci, responzivita

DigiZone.cz: ČRa DVB-T2 ověřeno: Hisense a Sencor

ČRa DVB-T2 ověřeno: Hisense a Sencor

Lupa.cz: Co se dá měřit přes Internet věcí

Co se dá měřit přes Internet věcí

120na80.cz: Horní cesty dýchací. Zkuste fytofarmaka

Horní cesty dýchací. Zkuste fytofarmaka

120na80.cz: Jak oddálit Alzheimera?

Jak oddálit Alzheimera?

Root.cz: Vypadl Google a rozbilo se toho hodně

Vypadl Google a rozbilo se toho hodně

Vitalia.cz: To není kašel! Správná diagnóza zachrání život

To není kašel! Správná diagnóza zachrání život

Lupa.cz: Insolvenční řízení kvůli cookies? Vítejte v ČR

Insolvenční řízení kvůli cookies? Vítejte v ČR

Měšec.cz: Finančním poradcům hrozí vracení provizí

Finančním poradcům hrozí vracení provizí

Vitalia.cz: Mondelez stahuje rizikovou čokoládu Milka

Mondelez stahuje rizikovou čokoládu Milka

Lupa.cz: Babiš: E-shopů se EET možná nebude týkat

Babiš: E-shopů se EET možná nebude týkat

Vitalia.cz: Jsou čajové sáčky toxické?

Jsou čajové sáčky toxické?

Vitalia.cz: 9 největších mýtů o mase

9 největších mýtů o mase

Root.cz: Certifikáty zadarmo jsou horší než za peníze?

Certifikáty zadarmo jsou horší než za peníze?

Vitalia.cz: Říká amoleta - a myslí palačinka

Říká amoleta - a myslí palačinka

Podnikatel.cz: EET: Totálně nezvládli metodologii projektu

EET: Totálně nezvládli metodologii projektu