# 13. hét: állapotgépek

Czirkos Zoltán · 2025.11.25.

Állapotgépekkel és reguláris kifejezésekkel kapcsolatos laborfeladatok.

Felkészülés a laborra:

- Az állapotgépekről szóló előadás megértése.
- A fájlkezelés áttekintése.
- A sztringekről, karakterkódokról tanultak átolvasása.

## 1. Bemenet karakterenként

Írj programot, amely a `sys` modul `sys.stdin.read(1)` függvényhívását használva karakterenként olvas be egy szöveget, majd minden beírt karakternek a karakterkódját kiírja a kimenetére! Ha pedig elfogyott a bemenet, akkor írja ki, hogy „bemenet vége”.

Figyeld meg a program működését:

- Amíg nem nyomsz entert egy beírt sorra, nem ír ki semmit.
- Utána viszont egyszerre a teljes sor karakterkódját. Miért van ez így?
- Hogy lépsz ki a programból? Nem(!!!) Ctrl–C-vel! Hogy adsz neki fájl vége jelet?
- Minek a karakterkódja a 10?

Ilyesmi kell legyen az eredmény:

    hello
    104
    101
    108
    108
    111
    10
    Bemenet vége.

Megoldás

    import sys

    while True:
        c = sys.stdin.read(1)
        if c == "": break
        print(ord(c))
    print("Bemenet vége.")

## 2. Ly számláló

Alább az előadás állapotgépes példakódját látod, az ly-számlálót.

    import sys

    def main():
        ALAP = 1
        L_VOLT = 2
        LL_VOLT = 3

        szaml = 0
        allapot = ALAP

        while True:
            c = sys.stdin.read(1)
            if c == "":
                break

            if allapot == ALAP:      # szöveg kezdete
                if c == "l":
                    allapot = L_VOLT

            elif allapot == L_VOLT:  # már volt egy l
                if c == "l":
                    allapot = LL_VOLT
                elif c == "y":
                    szaml += 1
                    allapot = ALAP
                else:
                    allapot = ALAP

            elif allapot == LL_VOLT: # két l volt
                if c == "l":
                    pass
                elif c == "y":
                    szaml += 2
                    allapot = ALAP
                else:
                    allapot = ALAP

        print(szaml, "darab ly volt.")

    main()

Rajzold meg a forráskód alapján állapotátmeneti és tevékenységeket tartalmazó táblázatát, vagy választásod szerint az állapotátmeneti gráfot!

Megoldás

Az „ly” számláló állapot- és tevékenységtáblája
| l | y | egyéb
--- | --- | --- | ---
alap | →l_volt | - | -
l_volt | →ll_volt | sz += 1, →alap | →alap
ll_volt | - | sz += 2, →alap | →alap

## 3. „Hejesírásreform”

Indulj ki az előző feladat kódjából!

Rajzolj egy új táblázatot, amelyben teljes egészében módosítod a tevékenységeket. Az új program feladata nem az `ly`-ok számlálása, hanem egy „hejesírásreform” végrehajtása. Ennek a beolvasott szöveget majdnem változatlanul kell kiírnia a kimenetre – azzal a különbséggel, hogy az `ly`-ok helyett `j`-t, a dupla `lly`-ok helyett `jj`-t kell kiírnia. Pl. `lyuk→juk`, `gally→gajj`, viszont `majom→majom` marad, és a `kulcs`, illetve a `hallgat` szavak is változatlanok maradnak. (Ezek a példák fontos állapotátmeneteket és tevékenységeket tesztelnek.)

Megoldás

| l | y | egyéb
--- | --- | --- | ---
alap | →l_volt | ki: c | ki: c
l_volt | →ll_volt | ki: "j" →alap | ki: "l", c →alap
ll_volt | ki: "l" | ki: "jj" →alap | ki: "ll", c →alap

A kiírásokat itt jól meg kell gondolni. Alapállapotban mindent kiírunk, kivétel az `l` betűt, mert az lehet egy későbbi `ly` része. `l_volt` állapotban bejövő `y` esetén kiírjuk a `j`-t; viszont bejövő egyéb karakter esetén az előző `l`-t is ki kell írni, és a mostanit is (ilyen szó: kulcs). `ll_volt` esetén pedig, ha bármi más jön, akkor az előző `ll`-t is ki kell írni (ilyen szó: hallgat).

A szövegfájloknál bevett szokás az, hogy a fájl legutolsó karaktere mindig egy újsor (\n) karakter. Ezt azonban sajnos nem mindenhol tartják be (és nem minden szövegszerkesztő tesz így).

Ha esetleg kap a fenti program egy olyan bemenetet, ahol a szöveg `'l'` betűre végződik (tehát nincs újsor, de még mondat vége jel sincs a bemenet végén), akkor hibázik; a kimeneten nem jelenik majd meg ez a betű.

## 4. A program futtatása parancssorból

Emlékezz vissza, mit tanultál az előadáson a programok parancssori futtatásáról, továbbá a fájlból és fájlba átirányításról!

Parancssor gyorstalpaló

A Start menü Futtatás… parancsát választva indítsd el a `cmd.exe` programot! (Windows 10 esetén: a Start gombra jobb klikk, és Futtatás, vagy `Windows + R`.) A továbbiakban a megjelenő konzolablakban kell dolgozni. A föl-le billentyűkkel kiválaszthatóak a korábban beírt parancsok, így nem kell egy dolgot többször begépelni.

Egy program indításához csak be kell írni a nevét. Pl.

    notepad.exe

A legtöbb konzolos alkalmazás, de gyakran az ablakokat használó programok is, kaphatnak paramétereket. Próbaképp írd be az alábbi sort. Mit tapasztalsz?

    notepad szoveg.txt

A mappát (folder) a parancssori parancsok a directory szóból származtatják (a mappák másik neve, magyarul könyvtár). A mappa tartalmának listázása:

    C:\Users\111111\> dir

Mappát váltani a `chdir` paranccsal, vagy ennek rövidebb változatával, a `cd`-vel lehet. Lépj egy szinttel feljebb:

    C:\Users\111111\> cd ..

Indíts egy parancssort, és futtasd le abból a helyesírásreform programodat! Ehhez operációs rendszertől függően a program nevét kell megadnod, vagy elé kell írnod azt, hogy `python3`, esetleg verziótól függően csak annyit, hogy `python` (próbáld ki, a te környezetedben melyik működik). Teszteld újra a programod pl. a lyuk és gally szavakkal, utána lépj ki belőle fájl vége jellel!

Végezd el a fájlból és fájlba átirányítást is! Erre is mutat példát a hivatkozott előadásrészlet. Hozz létre ehhez a jegyzettömbbel egy „ómagyar.txt” nevű fájlt, benne ly-okat tartalmazó szöveggel. Dolgoztasd ezt fel a programoddal, és irányítsd át a kimenetét az „újmagyar.txt” nevű fájlba!

Megoldás

A lényeg:

    python3 hejesirasreform.py <ómagyar.txt >újmagyar.txt

## 5. Grep

Írj programot, amely:

- Kér a felhasználótól egy reguláris kifejezést.
- Aztán beolvassa a szavak.txt fájlt... (Ezt mentsd le a gépedre!)
- ... és csak azokat a sorait írja ki, amire illeszkedett a megadott kifejezés.

Például:

    Kérem a regexet: ^..vé$
    kávé
    kővé
    tűvé

Ne feledd, a szavak beolvasásához szükség lehet a fájl kódolásának megadására: `encoding="utf-8"` paraméter az `open()` függvény számára.

Adj meg reguláris kifejezéseket, amelyekkel kilistázhatod az alábbi szavakat:

- Amelyekben van vicc (pl. vicces, de kaviccsal is)
- Almával kezdődnek (pl. almaecet, de simán alma is)
- Úgy végződnek, hogy hely (pl. táborhely, zabpehely)
- Négy betűsek, és mindkét középső betűjük n (pl. enni).
- Hat betűsek, fru-val kezdődnek (pl. fruska).
- Hosszú ó-val kezdődő és végződő szavak (pl. óceánjáró).
- Két hosszú ssz szerepel bennük (pl. összevisszaság).
- Szerepel bennük kétszer ugyanaz a négybetűs részlet (pl. tisztiszolga, rendszerszerű)
- Hat betűből állnak, kétszer ugyanaz (pl. bonbon)
- Hatbetűs tükörszavak (pl. lap pal)
- Hétbetűs tükörszavak (pl. találat)

Megoldás

    import re

    regex = input("Kérem a regexet: ")

    with open("szavak.txt", encoding="utf-8") as f:
        for s in f:
            s = s.rstrip()
            if re.search(regex, s):
                print(s)

- Vicc: `vicc`
- Alma: `^alma`
- Hely: `hely$`
- Enni: `^.nn.$`
- Fruska: `^fru...$`
- Óceánjáró: `^ó.*ó$`
- Összevisszaság: `ssz.*ssz`
- Tisztiszolga: `(....)\1`
- Bonbon: `^(...)\1$`
- Lappal: `^(.)(.)(.)\3\2\1$`
- Találat: `^(.)(.)(.).\3\2\1$`

## 6. Időpontok I.

Adott az alábbi három lehetséges formátum, amelyben időpontokat adhatunk meg:

- `23:17:06` – óra, perc, másodperc, mindegyik két számjeggyel;
- `15h 09m 53s` – itt is óra, perc, másodperc, mindegyik két számjeggyel;
- `10:15 AM` – itt az óra és a perc két-két számjeggyel. Ez a formátum 12 órás; `12:00 AM` = éjfél, `08:00 AM` = reggel 8, `12:00 PM` = dél, `05:00 PM` = a délután 5 óra, azaz 17 óra.

Adj meg reguláris kifejezéseket a három formátumhoz! Írj programot, amelyik folyamatosan sorokat olvas be a bemenetről, és megmondja, hogy az első, a második, vagy a harmadik formátumban van megadva az időpont! Ha esetleg egyikben sem (hibás a bemenet), akkor írd ki azt!

    Kérem az időpontot: 12:34:45
    Első formátum

    Kérem az időpontot: 05:34 PM
    Harmadik formátum

    Kérem az időpontot: 05:34 PMMMM
    Hibás

Megoldás

    import re

    while True:
        sor = input("Kérem az időpontot: ")
        if sor == "":
            break
        if re.match(r"^\d{2}:\d{2}:\d{2}$", sor):
            print("Első formátum")
        elif re.match(r"^\d{2}h \d{2}m \d{2}s$", sor):
            print("Második formátum")
        elif re.match(r"^\d{2}:\d{2} [AP]M$", sor):
            print("Harmadik formátum")
        else:
            print("Hibás")

## 7. Időpontok II.

Módosítsd az előző feladatban megírt reguláris kifejezéseidet úgy, hogy az egyes időpont formátumok esetén az időpontok értelmezéséhez szükséges adatokat egy zárójelezett blokk segítségével kigyűjtöd!

- Az első két formátumban: óra, perc, másodperc számok.
- A harmadik formátumban: óra, perc és az A vagy a P betű.

Írd ki ezeket a kimenetre!

Végül pedig definiálj egy időpont osztályt (óra, perc, másodperc) konstruktorral, és írj függvényt, amelynek paramétere a fenti három formátumok valamelyikékben adott időpont, visszatérési értéke pedig egy időpont objektum!

## 8. Lyuk

Fejleszd tovább a labor eleji „hejesírásreform” programot! Tanítsd meg az állapotgépednek, hogy kezelje helyesen a mondatot kezdő, nagybetűs `L` karaktert! Rajzold meg az új állapotátmeneti táblázatot!

Hány új állapot kell ehhez? Működik helyesen a programod, ha azt írod bemenetként, `Levél`? Vajon kell-e számolnod azzal, hogy mondat elejére két `j`-t kell írnod?

## 9. Mondatok nagybetűsítője

Írj állapotgépes programot, amely a beírt, csupa kisbetűkből álló szöveget úgy javítja ki, hogy minden mondat elején álló első betűt nagybetűre cseréli!

Megoldás

Mondat végét jelző írásjel után a következő betű nagybetű, de csak akkor, ha szóköz is jött. Figyelni kell, hogy az utána lévő szóköztől nem váltunk kisbetűs módra még! A gép alapállapota a nagybetűsítés, hiszen a bejövő szöveg legelső karaktere biztosan mondat elején van.

| . ! ? | szóköz, \n | egyéb
--- | --- | --- | ---
nagybetű | ki: c | ki: c | ki: c.upper() →kisbetű
kisbetű | ki: c →mondatvége? | ki: c | ki: c
mondatvége? | ki: c | ki: c →nagybetű | ki: c →kisbetű