Čtěte prosím tuto stránku celou a pozorně.
Napište program grep, který vybere jen ty řádky vstupního souboru, které odpovídají zadanému regulárnímu výrazu a tyto řádky zapíše do výstupního souboru.
Regulární výraz je speciální řetězec, který se skládá z normálnách znaků 'a'..'z', 'A'..'Z', '0'..'9' a speciálních znaků '?', '(', ')', '*', '{', '}', '|'. Každý regulární výraz odpovídá množině řetězců.
Znak '?' odpovídá jednomu libovolnému znaku.
Část výrazu, která je ohraničena kulatými závorkami a následována hvězdičkou, odpovídá libovolnému (i nulovému) počtu opakování výrazu v závorce.
Část výrazu, která je ohraničena množinovými závorkami odpovídá několika variantám, které jsou uvnitř závorek odděleny znakem '|'.
Příklady:
Regulární výraz | Obyčejný řetězec | Odpovídá ? |
---|---|---|
AAA | AAA | Ano |
AA | AAA | Ne |
AA | aa | Ne |
A?A | AbA | Ano |
A?A | AbbA | Ne |
?AA | AA | Ne |
(Ab)* | AbAbAb | Ano |
()*AA | AA | Ano |
()*bAA | AA | Ne |
A(?)*B | AahojB | Ano |
A()*B | AB | Ano |
Zacatek{Ahoj|Nazdar}Konec | ZacatekAhojKonec | Ano |
Zacatek{Ahoj|Nazdar}Konec | ZacatekAhojNazdarKonec | Ne |
X{A|}Y | XY | Ano |
X({A|B})*Y | XBABAY | Ano |
Jak je vidět z příkladů v tabulce, jednotlivé speciální prvky mohou být vnořené do sebe.
Program bude mít na vstupu dva soubory: vyraz.txt
a vstup.txt
.
Vstupní soubor vyraz.txt
má na jediném prvním řádku regulární výraz. Tento výraz není delší než 50 znaků a je vždy korektní.
Vstupní soubor vstup.txt
obsahuje řetězce, které se mají porovnat s regulárním výrazem. Na každém řádku je umístěný jeden řetězec. Žádný řádek není delší než 255 znaků.
Výstupní soubor vystup.txt
by měl po skončení programu obsahovat právě ty řádky ze vstupního souboru, které odpovídají regulárnímu výrazu.
Ukázkové vstupní a výstupní soubory si můžete stáhnout zde: data.tar.gz, nebo zde: data.zip.
Soubory ve formátu .tar
lze rozbalit například Total Commanderem, nebo pomocí programu tar.exe
příkazem "C:\>tar.exe xvf data.tar
". Více informací o použití programu tar
najdete zde.
Algoritmus spočívá v postupném procházení řetězce a regulárního výrazu. V řetězci i ve výrazu budeme pozici značit tečkou. Narozdíl od řetězce může být v regulárním výrazu více pozic, které jsou dosažitelné pomocí určitého počátečního úseku řetězce. Například:
{LAM.INO|LAM.A} LAM.ANI
Vlevo je regulární výraz a vpravo je řetězec.
Základní krok algoritmu spočívá v posunutí tečky v řetězci o jeden znak. Během základního kroku je také potřeba odpovídajícím způsobem "posunout" tečky v regulárním výrazu. Každá tečka regulárního výrazu se může buď obyčejně posunout, nebo rozmnožit, anebo může zaniknout. Algoritmus je zkonstruovaný tak, aby po dokončení každého základního kroku byly všechny tečky ve výrazu buď před obyčejným písmenem, nebo před otazníkem, nebo na konci řetězce.
Příklady:
{L.AMINO|L.AMA} L.AMANI {LA.MINO|LA.MA} LA.MANIV regulárním výrazu byly dvě tečky a obě se během základního kroku posunuly.
X.X{A|B|C}Y X.XZ XX{.A|.B|.C}Y XX.ZV tomto případě se rozmnožila tečka.
{A.BX|A.CX} A.BX {AB.X|ACX} AB.XV tomto případě se jedna tečka posunula a jedna zanikla.
Posunutí teček v regulárním výrazu není zcela triviální. Opět je potřeba manipulovat s tečkami, dokud nejsou všechny tečky před nějakým písmenem, nebo otazníkem. Aby se předešlo zacyklení, je potřeba označit křížkem pozice, které už byly označeny tečkou. Na pozici označenou křížkem se už tečka nikdy neumísťuje. Algoritmus probíhá podle následujícího schématu:
Příklad:
.A({{}|(?)*})*.A A.({{}|(?)*})*A. A+(.{{}|(?)*})*.A. A+(.{{}|(?)*})*.A. A+(+{.{}|.(?)*})*.A. A+(+{+{.}|.(?)*})*.A. A+(+{+{+}.|.(?)*})*.A. A+(+{+{+}+|.(?)*}.)*.A. A+(+{+{+}+|+(.?)*.}.)*.A. A+(+{+{+}+|+(.?)*+}.)*.A. A+(+{+{+}+|+(.?)*+}+)*.A. A+(+{+{+}+|+(.?)*+}+)*.A. A({{}|(.?)*})*.A.Tento příklad znázorňuje posunutí teček na základě předchozích pravidel. V bodě 2. se bere první vhodná tečka zprava.
Posunutí teček je možné na začátku programu spočítat pro každou pozici zvlášť a do tabulky poznamenat pro každou pozici seznam pozic na které tečka přejde. V dalším průběhu algoritmu je možné používat jen tuto tabulku.
hruska
(zavináč) popelka
(tečka) ms
(tečka) mff
(tečka) cuni
(tečka) cz
s předmětem zprávy PGM - grep
a s přiloženým souborem grep.pas
..exe
soubory.
Termín zadání: 8.11.2005.
Termín odevzdání: do 20.11.2005 včetně.
Termín odevzdání oprav: do 27.11.2005 včetně.
hruska
(zavináč) popelka
(tečka) ms
(tečka) mff
(tečka) cuni
(tečka) cz