Ruby po kapkách (9.) – úvod do regulárních výrazů
I kdybyste nechtěli vědět vůbec nic o Ruby, znalost regulárních výrazů vám sama o sobě významně zvýší kvalifikaci, zejména pokud jste vývojář nebo systémový administrátor. Tento extrémně silný nástroj pro práci s textovými daty se v posledních deseti letech dostal z okruhu esoterických nauk mezi běžně využívané techniky. Významně k tomu přispěla podpora v populárních skriptovacích jazycích nevyjímaje Ruby.
Co je to regulární výraz? Je to zápis určitého vzoru, který lze porovnávat s textem. Je to vlastně velmi speciální programovací jazyk, na základě kterého se vytvoří podprogram pro prohledávání textu. Regulární výraz je v Ruby reprezentován (jak jinak) objektem třídy Regexp
. Nejjednodušším způsobem, jak v vytvořit instanci tohoto objektu, je použití literálu. Regulární výraz se uzavře do dvou obyčejných lomítek. Pro porovnání textu s výrazem pak slouží speciální porovnávací operátor.
irb(main):001:0> /Ruby/.class
=> Regexp
irb(main):002:0> ‚Ruby‘ =~ /Ruby/
=> 0
irb(main):003:0> /Ruby/ =~ ‚Ruby‘
=> 0
irb(main):004:0> ‚Ruby‘ =~ /Duby/
=> nil
irb(main):005:0> ‚Ruby‘ !~ /Duby/
=> true
irb(main):006:0> ‚Duby Ruby‘ =~ /Ruby/
=> 5
V těchto triviálních příkladech vystupuje regulární výraz, který odpovídá řetězci ‚Ruby‘. Vidíme, že lomítkový literál skutečně vytváří Regexp
. Porovnávací operátor je definován jak na třídě Regexp
tak na třídě String
, takže porovnání je možné provádět oběma směry. K dispozici je také negovaný operátor. Výsledkem porovnání může být buď nil
v případě, že definovaný vzor nebyl v řetězci nalezen, nebo index znaku, kde začíná první nalezený výskyt textu odpovídajícího vzoru. Na první pohled jinak se chová negované porovnání, ale je třeba si uvědomit, že interpret Ruby v tomto případě provede normální porovnání (v našem případě s výsledkem nil
) a výsledek pak zneguje. Tím dostaneme onu hodnotu true
.
Základy jazyka regulárních výrazů jsou jednoduché. Většina znaků ve vzoru reprezentuje sama sebe a při porovnání se v textu prostě hledá odpovídající znak. Některé znaky jsou speciální a umožňují definici dodatečných podmínek. Nejčastěji se používá speciálních znaků, které určují povinnost či možné opakování předchozího znaku, seskupení znaků a možnost definovat výskyt třídy znaků namísto jednoho znaku.
irb(main):001:0> ‚Rub‘ =~ /Ruby/
=> nil
irb(main):002:0> ‚Rub‘ =~ /Ruby?/
=> 0
irb(main):003:0> ‚Rubbbby‘ =~ /Rub+y/
=> 0
irb(main):004:0> ‚Rubbbby‘ =~ /Rub*y/
=> 0
irb(main):005:0> ‚Ruy‘ =~ /Rub*y/
=> 0
irb(main):006:0> ‚Rubbbby‘ =~ /Rub{4}y/
=> 0
irb(main):008:0> ‚Rubbbby‘ =~ /Rub{1,5}y/
=> 0
irb(main):009:0> ‚Rubbbby‘ =~ /Rub{3,}y/
=> 0
Modifikátory povinnosti a opakování jsou definovány takto:
- ? – předcházející znak nebo skupina znaků se v textu nachází volitelně (0..1)
- + – předcházející znak nebo skupina znaků se v textu nachází jednou nebo vícekrát (1..n)
- * – předcházející znak nebo skupina znaků se v textu nachází volitelně jednou nebo vícekrát (0..n)
- {n} – předcházející znak nebo skupina znaků se v textu nachází právě n-krát (n..n)
- {m, n} – předcházející znak nebo skupina znaků se v textu nachází minimálně m-krát a maximálně n-krát (m..n); n je nepovinné
irb(main):001:0> ‚abcd‘ =~ /[a-z]+/
=> 0
irb(main):002:0> ‚abcd‘ =~ /[a-c]+/
=> 0
irb(main):003:0> ‚abcd‘ =~ /[a-c]{4}/
=> nil
irb(main):004:0> ‚abcd‘ =~ /[0-9]{4}/
=> nil
irb(main):005:0> ‚abcd‘ =~ /[0-9abcd]{4}/
=> 0
irb(main):006:0> ‚abcd‘ =~ /[ˆ0-9]{4}/
=> 0
Hranaté závorky definují třídu všech znaků, které se mohou objevit na nějaké pozici. Znaky lze definovat buď výčtem nebo rozsahem, kde mezi první a poslední znak se píše pomlčka. Výčet a rozsahy lze libovolně kombinovat a skládat za sebe. Pokud bychom chtěli pomlčku použít jako součást výčtu, musí být na prvním místě. Pokud dáme na první místo znak ^, bude třída definována opačně – jako všechny znaky, které nejsou součástí definice. V rámci regulárního výrazu má speciální použití znak tečka. Slouží jako zástupka pro jakýkoliv znak. Regulární výraz, který pasuje na jakýkoliv řetězec je pak kombinací tečky s nejsilnějším modifikátorem .*
Kulaté závorky seskupují vzory do skupin. Svislítko funguje jako logická spojka nebo. Pro větší kontrolu nad porovnáváním se také hodí znaky ^ a $, které odpovídají začátku a konci řetězce. Jinak k pozitivnímu výsledku porovnání stačí, aby vzor odpovídal části řetězce, jak jsme viděli v předchozím příkladu na řádku 2, kde definované množině znaků ve skutečnosti odpovídaly jen první tři znaky. Pokud bychom chtěli kterýkoliv speciální znak použít v regulárním výrazu doslova, museli bychom mu předřadit zpětné lomítko (což tím pádem platí i pro zpětné lomítko samotné).
irb(main):001:0> ‚Ruby‘ =~ /Perl|Python|Ruby/
=> 0
irb(main):002:0> ‚Duby Ruby‘ =~ /Perl|Python|Ruby/
=> 5
irb(main):003:0> ‚Duby Ruby‘ =~ /ˆ(Perl|Python|Ruby)/
=> nil
irb(main):004:0> ‚Duby Ruby‘ =~ /(Perl|Python|Ruby)$/
=> 5
Syntaxe regulárních výrazů umožňuje mnoho dalších funkcí, ale výše uvedené základy postačují pro mnoho praktických aplikací. Kde se tyto aplikace v Ruby skrývají? V první řadě po každém porovnání máme v několika předdefinovaných proměnných tyto řetězce:
- $& – část řetězce odpovídající vzoru
- $` – část řetězce před částí odpovídající vzoru
- $‘ – část řetězce za částí odpovídající vzoru
Tyto kryptické názvy jsou dědictvím jazyka Perl. Můžeme se jim vyhnout, pokud pracujeme s regulárnímy výrazy jako s objekty a voláme jejich metody.
irb(main):001:0> ‚Perl Python Ruby‘ =~ /Py[a-z]+/
=> 5
irb(main):002:0> $&
=> „Python“
irb(main):003:0> $`
=> „Perl „
irb(main):004:0> $‘
=> “ Ruby“
irb(main):005:0> r = Regexp.new(‚Py[a-z]+‘)
=> /Py[a-z]+/
irb(main):006:0> r.source
=> „Py[a-z]+“
irb(main):007:0> m = r.match(‚Perl Python Ruby‘)
=> #<MatchData:0x302afc8>
irb(main):008:0> m[0]
=> „Python“
irb(main):009:0> m.pre_match
=> „Perl „
irb(main):010:0> m.post_match
=> “ Ruby“
Jak je vidět, instanci Regexp
vytvoříme kromě již ukázaného způsobu s literálem také standardně voláním new
a jako parametr předáme zdrojový řetězec regulárního výrazu. Metoda match
třídy Regexp
provede porovnání a vrací výsledek ve formě objektu třídy MatchData
. Ten má definovaný indexovací operátor (hranaté závorky). Pod indexem 0 se skrývá řetězec odpovídající vzoru. Pokud bychom použili ve vzoru skupiny, byly by jednotlivé odpovídající části výsledku pod dalšími indexy. Index skupiny odpovídá vždy pořadí levé závorky, která uvozuje skupinu v regulárním výrazu. Při použití porovnání s literálem jsou tyto výsledku dostupné pod proměnnými $1, $2 atd. (Pozor $0 je název aktuálně bežícího procesu.)
Podívejme se nakonec na nějaké zajímavé metody, které používají regulární výraz jako parametr.
irb(main):001:0> ‚Perl Python Ruby‘.sub(/P[a-z]+/, ‚Ruby‘)
=> „Ruby Python Ruby“
irb(main):002:0> ‚Perl Python Ruby‘.gsub(/P[a-z]+/, ‚Ruby‘)
=> „Ruby Ruby Ruby“
irb(main):003:0> ‚123 aaa 678 zzz‘.scan(/[0-9]+/)
=> [„123“, „678“]
irb(main):004:0> [‚123‘, ‚aaa‘, ‚678‘, ‚zzz‘].grep(/[0-9]+/)
=> [„123“, „678“]
Pomocí metody sub
a gsub
můžeme nahrazovat části řetězců. První metoda nahrazuje první nalezený výskyt vzoru, druhá mětoda nahrazuje všechny výskyty. Metoda scan
prochází řetězec a generuje pole všech výskytů vzoru. Pokud není vzor nalezen, je pole prázdné. Máme-li kolekci, která je Enumerable
, máme k dispozici metodu grep
, která filtruje prvky podle toho, zda odpovídají zadanému regulárnímu výrazu.
Starší komentáře ke článku
Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.
Mohlo by vás také zajímat
-
Gaming na HDR monitoru: Stojí to za to?
12. srpna 2024 -
Nové trendy v doménách pro osobní projekty – DIY, LIVING a LIFESTYLE
9. prosince 2024 -
inPage AI: Jak na generování obsahu
18. července 2024
Nejnovější
-
Jak rozšířit úložiště Macu za pětinovou cenu?
16. prosince 2024 -
Nové trendy v doménách pro osobní projekty – DIY, LIVING a LIFESTYLE
9. prosince 2024 -
Jak chránit webové stránky před Web/AI Scrapingem
27. listopadu 2024 -
Jaký monitor je nejlepší k novému Macu Mini?
25. listopadu 2024