Java, Scala a regulární výrazy
Poslední dobou zjišťuji, že práce s regulárními výrazy v Javě a Scale je neuspokojivá, matoucí a API nikdy nenabízí přesně to, co bych chtěl.
Scala má krásně vypadající metodu Regex#replaceAllIn(String, Match =>
String)
. Na první pohled je všechno jasné: Každý výskyt regulárního výrazu v prvním argumentu je předán funkci Match => String
. Objekt typu Match
reprezentuje výskyt vzoru. Funkce ho vezme jako argument a vyprodukuje string,
který ho nahradí. Co by na tom mohlo být komplikovaného, žeano?
Jednoduché volání, které na první pohled vypadá jako identita, není bezpečné:
regex.replaceAllIn(str, (m: Match) => m.group(0))
m.group(0)
představuje celou shodu (ostatní indexy jsou uzávorkované výrazy v regexu, m.group(1)
jsou první závorky, m.group("named")
jsou
závorky pojmenované named
) .
V čem je problém?
V tom, že výsledek funkce se nepoužije verbatim pro nahrazení, ale je
interpretován. Každý výskyt řetězců $0
– $9
je interpretován jako podvýraz
a \$
znamená literál $
. Když vstupní data obsahují dolar následovaný
číslem, skončí to přinejlepším výjimkou, přinejhorším to udělá něco nečekaného.
A to není to, co by člověk čekal nebo chtěl. V dokumentaci je toto chování
dobře popsané, ale nepůsobí to, že by to tak mělo být.
Korektní verze by měla vypadat takhle:
regex.replaceAllIn(str, (m: Match) => Regex.quoteReplacement(m.group(0)))
Interpretace stringu je přežitek starších API, které nenabízely nahrazení přes fukci:
regex.replaceAllIn(str, replace: String)
V tomto případě nemůžu specifikovat logiku, která vypočítá nahrazovací string a proto jsou použity zpětné reference jako $0
– $9
. Ty poskytují aspoň nějakou
rudimentární flexibilitu.
Problém je v tom, že výsledný string musí být escapován a hned potom je
interpretován (pro $
a \$
), což je zbytečná práce, která stojí zbytečný čas.1
Ocenil bych jasné API s metodami jako
Regex#replaceAllByPattern
Regex#replaceAllByString
které jasně říkají, co dělají a uživatel si může vybrat přesně to chování, které potřebuje.
- Dodatek: I když má
quoteReplacement
měřitelnou režii, je téměř zanedbatelná. Implementace počítá s tím, že v naprosté většině případů není třeba nic escapovat.