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-za č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.dicpří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