9. hét: fájlkezelés
Czirkos Zoltán · 2024.10.29.
Szövegfájlok beolvasása. Formátumhibák kezelése.
Felkészülés a laborra:
- A fájlkezelésről szóló előadásrészlet áttekintése.
- A kivételkezelésről szóló előadás felelevenítése.
Adott a következő fájl: szavak_50.txt. Ez ötven darab magyar szót tartalmaz. Mentsd le ezt a fájlt a programod mellé, mert ezzel kell dolgoznia!
Írj programot, amelyik az alábbi feladatokat végzi el:
- Beolvassa a fájl szavait egy listába. Ügyelj arra, hogy ne legyen sorvége jel a szavak végén!
Legyen ez a
betoltes()
nevű függvény, amelynek paramétere a fájlnév, visszatérési értéke a szavak listája! Ellenőrizd a szavak számát, és magukat a szavakat is a beolvasás után! - Rendezd sorba a listát a beépített rendező függvénnyel,
sorted()
vagy.sort()
. - Írd ki egy másik fájlba csak a
k
betűvel kezdődő szavakat! Legyen ennek a neveszavak_kbetus.txt
, és legyen ugyanolyan a formátuma, soronként egy darab szó! Ellenőrizd a keletkező fájlt, pl. jegyzettömbbel! A fájlba írást végezze amentes()
nevű függvény, amelynek paraméterben lehet megadni a fájlnevet és a mentendő listát!
Ne felejtsd el sehol bezárni a fájlt! Használd a .close()
függvényt, vagy with
blokkot,
ahogy az előadáson szerepelt.
Karakterkódolások
Előfordulhat (tipikusan Windowson), hogy a fájlt a Python nem olyan karakterkódolással olvassa be, ahogy kellene. Ilyenkor az ékezetes betűk helyett furcsa karakterek jelennek meg, pl. „árvíztűrő tükörfúrógép” helyett „árvíztûrõ” vagy „ĂĄrvĂztĹąrĹ”. Ha ilyet létsz, az
open()
függvénynek add meg paraméterként a karakterkódolást is. Ez a többi feladatra is érvényes:open("szavak_50.txt", "rt", encoding="utf-8")
A témáról többet a karakterkódolások oldalon olvashatsz. Ez nem vizsgaanyag, de érdemes tudni róla, mert gyakran okoz galibát.
Azt is észreveheted, hogy a sorba rendezés is hibás: az ékezetes karakterrel kezdődő szavak a lista végére kerülnek. Ennek az az oka, hogy pl. a 'á' betű kódja az nagyobb, mint a 'z' betűjé.
Ezen úgy lehet segíteni, hogy egyrészt megadjuk azt a nyelvet, amin a sorbarendezés történik, ezt az ún. locale segítségével tehetjük meg, másrészt explicite kérjük a sorted függvényt, hogy a nyelvfüggő összehasonlító függvényt használja. Részletek a referenciában, többnyelvű program írásakor érdemes használni.print(ord('á'),ord('z')) 225 122
import locale szavak=['alma','áfonya','banán','szőlő'] locale.setlocale(locale.LC_COLLATE, 'hu_HU') # így csak a sorrend vagy LC_ALL, akkor mindent magyar nyelv szerint. print(*sorted(szavak)) print(*sorted(szavak, key= locale.strxfrm)) alma banán szőlő áfonya áfonya alma banán szőlő
Adott két fájl: m1.txt és m2.txt. Ezek a budapesti 1-es és 2-es metró megállóinak neveit tartalmazzák.
- Írj függvényt, amely paraméterként a fájl nevét kapja, és visszaad egy listát, amelyik a megállók neveit tartalmazza!
- Írj függvényt, amelyik paraméterként két fájl nevét kapja, és meghatározza, hogy át lehet-e szállni
az egyik metróvonalról a másikra! Ha igen, térjen vissza a megálló nevével, ha nem, akkor
None
értékkel! - Alakítsd át a programot egy teljes parancssori alkalmazássá, amelynek két paramétere van ami a metrómegállók neveit tartalmazza! Ehhez szükséged lesz a parancssori paraméterek kezelésére.
Teszteld a programod további adatokkal! A másik két metróvonal: m3.txt és m4.txt. Az M1–M4 között nincs átszállási lehetőség, a többinél van.
A következő fájl egy régebbi évfolyam első kis ZH-n elért eredményeit tárolja: kzh_pontszam.txt. A fájlban a pontszámok ömlesztve vannak, minden sorban egy szám.
- Olvasd be a fájlban tárolt számokat egy listába! Ügyelj arra, hogy ezeket
int
-té kell alakítanod. Legyen ez abeolvas()
függvény, amelynek paramétere a fájlnév, értéke a pontszámok sorozata! - A legkisebb pontszám 0, a legnagyobb 10. Készítsd statisztikát: hány 0 pontos, hány 1 pontos, ... ZH lett!
Tedd ezt a
stat()
nevű függvénybe, amely paraméterként az összes pontszámok kapja, visszatérési értékként pedig a statisztikát állítja elő! (Pontszámmal indexelhető lista, amely létszámokat tartalmaz.) - Írd ki a statisztikát, pontszámok szerinti eloszlást a
stat_kiir()
függvényben, amely paraméterként a[pontszám]→létszám listát kapja!
Írd ki azt is, hogy hány sikeres ZH lett, ahol a pontszám legalább 4! Hány %-a ez az évfolyamnak? - Ügyelj itt is a fájl bezárására! Hívd meg a főprogramból a függvényeket, hogy megjelenjen az eredmény!
Ez kell legyen az eredmény:
1 db 0 pontos 3 db 1 pontos 10 db 2 pontos 11 db 3 pontos 9 db 4 pontos 12 db 5 pontos 14 db 6 pontos 41 db 7 pontos 11 db 8 pontos 18 db 9 pontos 29 db 10 pontos Átment: 134 fő, 84.28%
Megoldás
A legegyszerűbb megoldás a fájl sorainak beolvasására a for
ciklus, mert ez szövegfájlból
eleve sorokat ad:
with open(fajlnev) as f:
for sor in f:
szamok.append(int(sor))
A teljes program:
def beolvas(fajlnev):
"""Beolvassa a pontszámokat, és int-ek listáját adja
vissza, pl. [10, 7, 8, 9, 10, 7, 5, 3]."""
szamok = []
with open(fajlnev) as f:
for sor in f:
szamok.append(int(sor))
return szamok
def stat(pontszamok):
"""Pontszámok szerinti statisztikát készít, és ezt adja vissza,
pl. [5 (darab 0 pontos), 7 (darab 1 pontos), ...]."""
db = [0] * 11 # [0, 0, 0, ...]
for p in pontszamok:
db[p] += 1
return db # [5, 7, 3, ...]
def stat_kiir(db):
"""Kiírja az előállított statisztikát. A db nevű lista
indexei a pontszámok, értékei a létszámok."""
for p in range(0, 10+1):
print("{:2} db {:2} pontos, {}".format(db[p], p, "*" * db[p]))
# hány fő írta meg: a létszámok összege.
osszes = sum(db)
# átment: a lista 4 pont fölötti részében a létszámok összege.
atment = sum(db[4:])
print("Átment: {} fő, {:.4}%".format(atment, atment/osszes * 100))
def main():
pontszamok = beolvas('kzh_pontszam.txt')
db = stat(pontszamok)
stat_kiir(db)
main()
Tedd félre kicsit a pontszámos programot, később még lesz vele feladat!
Írj egy függvényt, amelyik paraméterként egy sztringet vesz át, és visszatérési értékében megmondja, hogy szám van benne vagy
nem szám! Pl. szam_e("123")
értéke True
, szam_e("almafa")
értéke viszont False
kell legyen. Emlékezz vissza: ezt legegyszerűbben úgy lehet megoldani, hogy számmá
konvertálod a kapott sztringet, és elkapod a kivételt, ha kell.
Megoldás
def szam_e(s):
try:
int(s)
return True
except ValueError:
return False
def main():
s = input("Írj be valamit: ")
print("Szám." if szam_e(s) else "Nem szám.")
main()
A kzh_pontszam_hibas.txt fájl ugyanazokat az adatokat tartalmazza, mint az előző – azzal a különbséggel, hogy ebben hibás sorok is vannak. Némelyik üres, máshol beficcen egy-egy szó vagy egy valós szám, ami nem való a helyes adatok közé.
A feladatod úgy módosítani az előző feladat beolvasó függvényt, hogy kihagyja ezeket a hibás sorokat, és csak a helyes adatokat adja vissza a listában!
Tipp
Egy sor beolvasásánál három eset lehetséges:
- Vége lett a fájlnak, nem lehetett sort beolvasni.
- Volt mit beolvasni, de az hibás, pl. üres sor, egy szó, vagy egy valós szám.
- Helyes pontszámot lehetett kapni a fájlból.
A lényeg, hogy a két lehetséges hibát el kell választani egymástól. Amikor a fájl végére értünk, akkor a
.readline()
üres sztringet ad – erre lehet ciklust építeni.
Megoldás
Fájl végét ellenőrző ciklussal:
def beolvas(fajlnev):
szamok = []
with open(fajlnev) as f:
while True:
sor = f.readline()
if sor == "":
break
try:
szamok.append(int(sor))
except ValueError:
pass
return szamok
Ha for
ciklussal kezeltük a fájl sorait, akkor eleve megállt a ciklus a fájl végén. A konverziós
hibát le kell kezelni ilyenkor is:
def beolvas(fajlnev):
szamok = []
with open(fajlnev) as f:
for sor in f:
try:
szamok.append(int(sor))
except ValueError:
pass
return szamok
A zheredmeny.txt fájl, ahogy a tartalmán is látszik, kitalált neveket, NEPTUN kódokat és dolgozatok eredményeit tárolja:
DHSF7F:Fül Elek:24 ZPD9PY:Szőke Barna:17 LGNUKG:Metall Ica:3 XMOO5D:Füle Imre:12 ...
Minden sor egy dolgozat adatait tárolja: NEPTUN kód, név és pontszám; mindezek kettősponttal elválasztva.
- Definiálj osztályt, amelyik egy nevet, NEPTUN kódot és pontszámot tárol!
- Írj függvényt, amelyik paraméterként kapott nevű fájlból beolvassa ezeket az adatokat egy listába!
Ügyelj itt is, hogy
int
-ként tárold a pontszámot.
Hozz létre egy második listát, amelyik ugyanazokat a dolgozat objektumokat tartalmazza, mint az előbbi! Nagyon figyelj arra, hogy ez mit jelent: nem kell beolvasni még egy fájlt, nem kell létrehozni új dolgozat objektumokat, hanem csak egy új listába be kell tenni ugyanazon dolgozatok referenciáit!
A következőképp tudod ellenőrizni, hogy ezt jól csináltad-e:
lista1 = ...
lista2 = ...
print(lista1 is lista2) # False, mert két külön listáról van szó
print(lista1[0] is lista2[0]) # True, ugyanazok az objektumok vannak benne
lista1[0].pontszam = 27 # Reklamált, változott a pontszáma
print(lista2[0].pontszam == 27) # True, mert ugyanaz a dolgozat objektum
Rendezd ezek után a két listát; egyiket név szerint, másikat pontszám szerint! Írd ki az eredményt a képernyőre, ellenőrizd az adatokat!
Dolgozz tovább a kis ZH pontszámos feladattal! Oldd meg teknőcgrafikával, hogy egy oszlopdiagramot kapj az eredményekről! Az x tengelyen a pontszámok vannak, az y tengely pedig a létszámot mutatja, hogy hányan értek el annyi pontot. Ehhez hasonló kell legyen az eredmény:
Megoldás
import turtle
def beolvas(fajlnev):
"""Beolvassa a pontszámokat, és int-ek listáját adja
vissza, pl. [10, 7, 8, 9, 10, 7, 5, 3]."""
szamok = []
with open(fajlnev) as f:
while True:
try:
szamok.append(int(f.readline()))
except:
break
return szamok
def stat(pontszamok):
"""Pontszámok szerinti statisztikát készít, és ezt adja vissza,
pl. [5 (darab 0 pontos), 7 (darab 1 pontos), ...]."""
db = [0] * 11 # [0, 0, 0, ...]
for p in pontszamok:
db[p] += 1
return db # [5, 7, 3, ...]
def teglalap(w, h):
turtle.begin_fill()
turtle.forward(w)
turtle.left(90)
turtle.forward(h)
turtle.left(90)
turtle.forward(w)
turtle.left(90)
turtle.forward(h)
turtle.left(90)
turtle.end_fill()
def stat_rajz(db):
turtle.speed(0)
turtle.hideturtle()
turtle.up()
turtle.goto(0, 0)
turtle.down()
for letszam in db:
turtle.forward(5)
teglalap(15, letszam * 5)
turtle.forward(15 + 5)
def main():
pontszamok = beolvas('kzh_pontszam.txt')
db = stat(pontszamok)
stat_rajz(db)
turtle.done()
main()
Készíts a „dolgozatok” című feladat adataiból is grafikont, az előzőhöz hasonlóan!
Ha a laborfeladatokkal elkészültél, dolgozz a példatárban lévő feladatokon, a szorgalmi feladatokon, a minta vizsgán, vagy a házi feladatodon.
Informatikusok próbálják lekódolni az Algoritmusok és gráfok tárgyon tanult algoritmusokat. Ezzel két legyet lehet ütni egy csapásra, mert két tantárgyat is gyakoroltok egyszerre.