úterý 15. července 2025

grep v základním režimu reg. expr. (BRE) nepoužívá kvantifikátor +

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ísmena a-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ásleduje n, 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:

  1. 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“).
  2. 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í

  1. Pro grep: Použijte grep -E, jak je uvedeno výše.
  2. 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())
    
  3. Kontrola souboru: Zkontrolujte obsah cs_CZ.dic příkazem cat -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

Python: Přístup Aho–Corasick & Memory‑map (mmap) + bufferované čtení bez .split() a .strip()

  Dobře, pojďme se kouknout na dvě pokročilé techniky, které (pravděpodobně) ještě nemáš nasazené, a které ti během tisíců až milionů řádků...

Štítky

.profile adm administrace Adobe Aho-Corasick AI akcelerace alfa transparence analýza AND any aplikace apt ar archiv asociativní pole atomicity audio autentifikace awk balíčkovací systém bash beacon beacon_hint benchmark Bézierovy křivky bezpečnost biblehub BJT blogger boolean brainstorming BRE buffer buffering bufferované čtení Cache-Conrol Cloudflare code Collector Cut-off ColorManager colorpicker common compare config cookies CPU CPU pipe css CSS3 curl current code cut čas data loss data lost data transfer reliability datasheet datetime.strptime deb deb-systemd-helper debian debián depricated development dict dioda diody dpkg dpkg -S dpkg-deb drivers EBO Emitter Cut-off Current eps ETag exclude exec Expires extrakce jediného extrakce názvu balíčku souboru extrakce obrázků extrakce souboru .deb fflock fflush ffmpeg FIFO file read file write file_get_contents file_get_contents/file_put_contents file_put_contents filter find first_install.sh flock Fly-back dioda font-face fóra fotorezistor fread functions funkce fwrite gate gate drive GDVfs gedit gedit-common geolokace getdata Ghostscript GIO glib gnome gnome settings GNU Privacy Guard gnupg gpg gradient-background grafika grep grep -v groupadd grub grub update gs gsettings gtk gtk.css gtk+ hebrejština history hlavičky HS html html 5 https hudba hunspell charakterizace chatGPT chroot chyba ICES IGBT Image img sizes img srcset impedance implementace imshow inference inkscape inrush current install jalový výkon javascript javescript jednocení seznamů js jsonData kapacita součástek koeficient zesílení komponenty xFce komunikace se serverem koncept konfigurace kontejner korekce barev Krita KSF kvantifikátor Last-Modified lazy caching led LEFT JOIN librosa ligatury light-locker lightdm linux list log maják manuál maskování maskování služby masky matplotlib Max-Age measure memory měření MFCC MFCC koeficienty mint Mint 21.3 Mint xFce míry modules moralizace morphologie MOSFET mount moviepy mysql náběhový proud napěťová ochrana nastavení šablony návod nel Network Error Logging NLP normalizace šedi po resize not Notifications NTFS nth-child oblasti oblékání ochrana okruhy přátel OpenVINO IR formát oprava oprava balíčku optočlen org.gnome.desktop.screensaver org.gnome.nm-applet ořezové masky OSHB otázky otázky_jazyky otázky_moralismu_řešení ovladače panely parsování path pdf personifikace photorec php php 4 php 5 php 6 php 7 php 8 phpbb phpBB3 PipeWire pitch plus PN přechody pnp pole Policykit postscript práva profilování program prune průraz přeinstalování přepěťová ochrana přepolování příkazy připojení k síti připojení k wifi pseudokódd pstoedit PulseAudio PWM regulátory pydub python python3 pytorch ramdisk RBE RDSon read reaktance rectifier regex regulace vstupního napětí reinstall relyability remount replace restore reverzní geolokace RIGHT JOIN rm role rozvržení disků pro OS linux a data databází řešení samba scan scroll sdílení sdílení souborů Sec-Fetch-Dest Sec-Fetch-Mode Sec-Fetch-Site Sec-Fetch-User Secure Shell sed Set Cookie show-manual-login show-remote-login shunt schemas schémata schottka skript skupiny sledovanost sloupce slučování seznamů služby small song sort soubory soundfile spínané zdroje spínání splines split spojování správa diskových zařízení SQL ssh stabilizace napětí stahování stíny stream string strojové učení stropové učení supplicant svg syntax systemctl systemd-logind T5 tabulka tabulky Tangentové úsečky tar témata tepelná ztráta terminologie test text-shadow themes thermal runaway time timestamp tkinter tr transistor transition tranzistor tranzistory tuple tvorba otázek TVS ubuntu účiník udiskd udisks unconfined underrun unity-greeter update usermod uživatelé va charakteristika vala vektorová grafika Vgs video Vth výkon vynechání adresářů vývoj while wpa wpa_supplicant wrapovací funkce x xandr xapp-watt xargs -I xed xed-common xfdesktop xml XOR Xorg Xorg Thumbnails xrandr závislosti zdánlivý výkon zdroj zenerka zenerovo napětí zip zip archiv zkratky zpomalení zpracování textu zrychlení Žalmy