Regulární výrazy, použití pro validaci polí a pro vyhledávání informací v rozsáhlých nestrukturovaných datech
Regularní výrazy, anglicky Regular Expressions, zkráceně též RegExp či regex je omezený programovací jazyk sloužící k vyhledávání v textu či verifikace formátu.
Umožňují jednoduchou definici formátu, díky své omezenesti nejsou složité na implementaci či naučení, a použivají se ve vícero programovacích jazycích, většinou jen s malými rozdíli ve specifických případech.
Základy jazyka RegExp
Znaky které nemají speciální chování se zapíšou přímo a hledají daný znak.
[] se používají pro sady znaků. Například [abc] je jedno z písmen a, b nebo c.
Umí též škálu znaků, napříklaz [a-z] je celá anglická abeceda malých písmen. Alfanumerické možností, i pro velké malé písmena se dají zapsat [a-zA-Z0-9].
Jsou omezené co do nich lze vložit - nejsou to skupiny, a neberou skupiny do sebe a vždy jsou nahrazeny jedním znakem, avšak ten se může lišit opakuje-li se sada. [ab]{2} vezme aa, ab, ba a bb.
Skupiny se dají negovat pomocí ^ na začátku, tedy skupina [^a] vybere jakýkoliv znak krom a.
{} opakuje předchozí znak, sadu znaků či skupinu (o nich později). Přímo číslo uvnitř závorek vybere přesný počet, například a{3} vybere právě aaa.
Čísla se mohou zapsat dvě, a v tu chvíly vyberou škálu, a{1,3} vybere a, aa i aaa.
Zapíše-li se čárka ale ne vynechá se jedno z čísel, bere se do extrému - vynecháli se první číslo, je bráno jako 0 a více, tudíž předchozí znak se nemusí objevit ani jednou, vynechá-li se druhé číslo, horní hranice počtu je neomezená. a{,2} vybere prazdný text, a i aa. a{1,} vybere alespoň jedno a, ale může jich být i tisíc.
() označuje skupinu. Tato skupina se poté bere jako jeden objekt, který se může opakovat a podobně. Například (aaa) je stejné jako aaa, ale může se použít i (abc){3} které vybere abcabcabc. Skupiny se také za určitých podmínek dají “vytáhnout” - při nahrazení textů je běžně možnost je referencovat pomocí $ a čísla skupiny, kde číslem 0 je označen celý text. Například se dá použít regex \$([0-9]{1,}) a nahradit ho šablonou $1 CZK a text Cena $100 se změní na Cena 100 CZK. $ má speciální chování a musí se proto “escapovat” zpětným lomítkem.
| je použit jako “NEBO”. Například a|b je shodné s [ab] v tom, že vybere právě jedno písmeno z a nebo b. Jeho síla však tkví v tom že to není sada znaků, nýbrž vybírá mezi objekty, a tudíž lze použít i na řetězce či skupiny - a{3}|ba(cd|ef) vybere aaa, bacd nebo baef.
Posledním základním znakem je \ které slouží k “escapování” písmen. Jednak slouží k tumo aby nám umožnil zapsat jiný speciální znak aniž by ho regex nějak zpracovával. Cheme-li najít text ((, použijeme na něj regex \(\(. Druhak ale slouží k zapsání jiných speciálních znaků.
Rozšíření
V regexu se běžně používají další speciální znaky, ale většinou slouží akorát jako jiné označení některých z předchozích chování.
? je totéž co {0,1}, tudíž znamená že předchozí objekt se v textu nachází maximálně jednou, ale nemusí tam být. ab? vybere texty a a ab.
+ je obdoba {1,}, aneb alespoň jednou, bez horní hranice.
* je zase {,}, bez minimálního či maximálního počtu, předchozí objekt se může a nemusí vyskytovat.
. je jakýkoliv znak krom nového řádku. Tedy [^\n]
\d je jakékoliv číslo, i.e. [0-9]. Tyto znaky se mohou použít i uvnitř sad.
\s je netisknutelný znak, tedy například mezera či nový řádek.
\w je [a-zA-Z0-9_].
\D, \S a \W jsou opaky, tedy nečísla a tisknutelné znaky.
^ označuje začátek řetězce, $ jeho konec. Nejdřív nahoře, pak zeshora dolů.
Dále jsou “klasické” escape sekvence používané i mimo regex - \t je tabulátor, \n je nový řádek, \r je carriage return (sekvence nového řádku na windows je \r\n zatímco na většině ostatních systémech jen \n).
Pro vyhledání \ se použije \\
Módy
Regex se může používat ve vícero módech, u kterých záleží na použité knihovně a funkci.
Tři nejpoužívanější jsou multiline, global a case insensitive.
multiline zpracovává každý řádek samostatně - nelze vyhledávat napříč řádky, ^ označuje vždy začátek řádku a $ jeho konec.
global vyhledá všechny výskyty a neskončí prvním. V závislosti na knihovně se výskyty mohou či nemohou překrývat. Dále v global módu nemusí jít vždy získat seznam vybraných skupin.
case insensitive ignoruje velikosti písmen, takže už se nemusí psát [a-zA-Z] ale stačí [a-z].
Příklady použití
Jedna z nejčastějších operací je .* která vybere jakýkoliv znak v jakémkoliv počtu. Například chceme-li vybrat klasickou větu (bez háčku a čárek), můžeme použít regex [A-Z].*\.
Chcete-li například v markdown vyhledat titulky, vyhledejte řádky začínající s #, například ^#{1,6} .+ v multiline módu, třeba přes sed či grep (grep je v základu multiline mode). Toto vyhledá řádky které začínají (^) alespoň jedním # a maximálně 6, které za sebou mají mezeru, a následně mají alespoň jeden znak jiný než nový řádek.
Příklad použití v C#:
using System;
using System.Text.RegularExpressions;
class Program
{
static void Main()
{
// Jako příklad řádek z logu
string input = "[123] abcdef";
// regex výraz který chceme filtrovat
string pattern = @"\[(\d+\)] (.*)";
Regex regex = new Regex(pattern);
// match = výsledek aplikování výrazu na vstup
Match match = regex.Match(input);
if (match.Success)
{
// match.Groups je list/array group které se našli v regexu
string time = match.Groups[1].Value;
string content = match.Groups[2].Value;
Console.WriteLine($"Čas: {time}");
Console.WriteLine($"Obsah: {content}");
}
else
{
Console.WriteLine("Špatný formát!");
}
}
}
Rady na konec
Existují dobré online nástroje na kontrolu či popis regulárních výrazů, například regexr.com.
Buďte opatrní při kontrole uživatelského vstupu regexem.
Regex je jednoduchý dokud je jeho použití jednoduché, problémy s regexem nastávají snaží-li se někdo dělat s ním složité operace.
Zde, stejně jako v jiných případech platí, nevymýšlejte kolo znova. A častokrát regex není ten správný nástoj.
Příklady:
- Kontrola uživatelských jmen. Přece má každý alespoň 3 znaky a velké písmeno, tak dám regex
[A-Z]\S{2,}. A pak přijde Čapek,Abči jenAa neprojdou testem. - Kontrola emailů. Oh však stačí
[A-Za-z0-9]+@[a-z]+\.[a-z]{2,3}. Do baru přijde/@1.1.1.1,"Pan barman"@[::1],info@local,me@100.100ainfo@i❤️.wsa zeptají se kdo z nich není validní. - Kontrola url. I prohlížeče to občas pokazí a využije se pro zranitelnost. Například, kterou doménu má otevřít
http://google.com/something/other@gogl.com/path#something, dále viz email. - Kontrola data či času. Jsou dny které neexistují i když se dají zapsat, podle lokace kde jste. Problém s časovými zónami se týká i jejich zápisu.
- Kontrola IPv4 adres. Však to jsou jen 4 čísla za sebou,
(\d{1,3}\.){3}\d{1,3}stačí. Jen nezapomeňte že musí být do 255. Podle počtu oktetů. IPv4 adresa může být ze dvou čísel které jsou každé do 65535. Chcete na to psát regex? A jak dlouhý bude?
