Otázka se týká problému s regulárním výrazem (regexem) v Pythonu a případně v příkazu grep
, kde hledáte slovo „ženu“ pomocí vzorce ^[a-zščťž][aeiouyáéíóúýůě]+nu/.*
, ale tento vzorec nenachází očekávaný výsledek „ženu/BN“, přestože by podle vás měl fungovat díky přítomnosti znaku +
. Pojďme si to podrobně rozebrat a vysvětlit, v čem je problém a jak ho vyřešit.
Rozbor problému
Nejprve si rozložíme váš regex ^[a-zščťž][aeiouyáéíóúýůě]+nu/.*
na jednotlivé části, abychom pochopili, co přesně hledá:
^
: Začátek řetězce.[a-zščťž]
: Jeden znak z této množiny, což zahrnuje malá písmenaa-z
a české znakyš
,č
,ť
,ž
. Tedy napříkladž
zde sedí.[aeiouyáéíóúýůě]+
: Jeden nebo více samohlásek z této množiny (včetně českých samohlásek jakoá
,é
,ů
,ě
). Znak+
znamená „alespoň jedna“.nu/
: Přesná posloupnost znakůn
,u
,/
..*
: Nula nebo více libovolných znaků až do konce řetězce.
Pro slovo „ženu/BN“ by to mělo fungovat takto:
^
: Začátek řetězce – ano.[a-zščťž]
:ž
– ano, je v této množině.[aeiouyáéíóúýůě]+
:e
– ano, je to samohláska, a+
vyžaduje alespoň jednu, takže to sedí.nu/
: Následujen
,u
,/
– ano, přesně odpovídá..*
: ZbýváBN
– ano, odpovídá libovolným znakům.
Teoreticky by tedy regex ^[a-zščťž][aeiouyáéíóúýůě]+nu/.*
měl slovo „ženu/BN“ najít, protože splňuje všechny podmínky: začíná písmenem ž
, má jednu samohlásku e
(což stačí pro +
), a pak následuje nu/
a zbytek.
Proč to nefunguje?
Ve vaší otázce uvádíte dva příkazy:
cat cs_CZ.dic | grep "^[a-zščťž][aeiouyáéíóúýůě]*nu/.*" | ...
- Tento příkaz najde „ženu/BN“ (společně s dalšími, např. „tnu/BN“, „žnu/BN“).
cat cs_CZ.dic | grep "^[a-zščťž][aeiouyáéíóúýůě]+nu/.*" | ...
- Tento příkaz „ženu/BN“ nenajde.
Rozdíl je mezi *
(nula nebo více) a +
(jedna nebo více). Ale protože „ženu/BN“ má jednu samohlásku e
, měl by fungovat i regex s +
. Proč tedy nefunguje?
Klíčový důvod: grep
a základní regulární výrazy (BRE)
Problém spočívá v tom, že příkaz grep
bez přepínače -E
používá základní regulární výrazy (Basic Regular Expressions, BRE), nikoli rozšířené regulární výrazy (Extended Regular Expressions, ERE). V BRE má znak +
jinou funkci:
- V BRE není
+
kvantifikátor („jedna nebo více“), ale je považován za literál, tedy hledá skutečný znak+
v textu. - V ERE (povolené pomocí
grep -E
) je+
kvantifikátor, jak očekáváte.
Takže váš druhý regex ^[a-zščťž][aeiouyáéíóúýůě]+nu/.*
v grep
bez -E
hledá:
- Začátek řetězce.
- Jeden znak z
[a-zščťž]
. - Jeden znak z
[aeiouyáéíóúýůě]
. - Literál
+
(který v „ženu/BN“ není). - Pak
nu/
a cokoli dalšího.
Protože v „ženu/BN“ není znak +
mezi e
a nu/
, regex neodpovídá a nic nenajde.
Naopak první regex s *
funguje, protože v BRE je *
kvantifikátor („nula nebo více“) a správně interpretuje [aeiouyáéíóúýůě]*
jako nula nebo více samohlásek, což „ženu/BN“ splňuje.
Řešení pro grep
Aby váš druhý regex fungoval podle očekávání (hledal alespoň jednu samohlásku), musíte použít grep -E
, který povolí rozšířené regulární výrazy. Upravte příkaz takto:
cat cs_CZ.dic | grep -E "^[a-zščťž][aeiouyáéíóúýůě]+nu/.*" | grep -vE "/HRIN|/FI|/PI|/[HRPQEFLW]+$" | grep -vE "^(pro|na|do|u|roze?|přede|př[ei]|po|za|vy|ode?|obe?|znovu|spolu)"
Tento příkaz správně rozpozná +
jako „jedna nebo více samohlásek“ a najde „ženu/BN“, protože e
je přítomna.
A co Python?
Ve vaší otázce zmiňujete „regex v Pythonu 3“. V Pythonu modul re
používá rozšířené regulární výrazy implicitně, takže +
je vždy kvantifikátor. Pokud tedy používáte tento kód:
import re
text = "ženu/BN"
pattern = r"^[a-zščťž][aeiouyáéíóúýůě]+nu/.*"
if re.match(pattern, text):
print("Našlo to!")
else:
print("Nenašlo to.")
Výstup bude „Našlo to!“, protože regex funguje správně a „ženu/BN“ odpovídá vzorci.
Pokud vám to v Pythonu nefunguje, mohl by být problém v:
- Kódování: Ujistěte se, že váš soubor/skript je v UTF-8, aby české znaky (
ž
,ě
) byly správně rozpoznány. - Textu: Ověřte, že testovaný řetězec je skutečně „ženu/BN“ bez skrytých znaků (např. mezery, konce řádků).
Ověření a doporučení
- Pro
grep
: Použijtegrep -E
, jak je uvedeno výše. - Pro Python: Regex by měl fungovat, pokud je text správně formátovaný. Můžete vyzkoušet:
import re with open("cs_CZ.dic", encoding="utf-8") as f: for line in f: if re.match(r"^[a-zščťž][aeiouyáéíóúýůě]+nu/.*", line.strip()): print(line.strip())
- Kontrola souboru: Zkontrolujte obsah
cs_CZ.dic
příkazemcat -A cs_CZ.dic
, zda neobsahuje skryté znaky.
Závěr
Důvod, proč druhý regex ^[a-zščťž][aeiouyáéíóúýůě]+nu/.*
neextrahuje „ženu/BN“ v příkazu grep
, je ten, že bez -E
je +
považován za literál, nikoli kvantifikátor. Po odstranění +
nebo nahrazení *
to funguje, protože *
je v BRE správně interpretováno. Použitím grep -E
zajistíte, že +
bude znamenat „jedna nebo více“, jak očekáváte.
Doufám, že toto vysvětlení vyřeší vaši záhadu! Pokud máte další otázky, neváhejte se ozvat.
Žádné komentáře:
Okomentovat