Kap 4.4
Miniuppgift: filbaserat program
Bygg en receptsamling som sparar data mellan programmets körningar.
Mål
Du kombinerar tidigare kunskaper
- funktioner och tydlig programstruktur
- menyer, villkor och loopar
- skrivning och läsning av textfiler
try/exceptvid filfel- testning av flera programflöden
Uppgiften
Receptsamling
Användaren ska kunna:
- lägga till ett recept med namn och ingredienser
- visa alla sparade recept
- avsluta programmet
Recepten ska finnas kvar nästa gång programmet startas.
Planera först
Vilka delar behöver programmet?
visa_meny()
lägga_till_recept()
visa_recept()
main()
- En funktion för varje tydlig uppgift.
main()kopplar samman funktionerna.
Lagring
Bestäm ett enkelt filformat
Pannkakor|mjölk,ägg,mjöl
Tomatsoppa|tomater,lök,vatten
|skiljer receptnamnet från ingredienserna.- Komma skiljer ingredienserna från varandra.
- Ett recept sparas per rad.
Konstant
Filnamnet används på flera platser
RECEPTFIL = "recept.txt"
Namnet skrivs med stora bokstäver eftersom värdet är tänkt att vara konstant.
Både skrivfunktionen och läsfunktionen använder samma filnamn.
Meny
Visa användarens alternativ
def visa_meny():
print("\nMeny:")
print("1. Lägg till recept")
print("2. Visa recept")
print("3. Avsluta")
Funktionen ansvarar bara för att skriva ut menyn.
Mallkod
Bygg programflödet först
def main():
while True:
visa_meny()
val = input("Välj ett alternativ: ")
if val == "1":
pass
elif val == "2":
pass
elif val == "3":
break
else:
print("Ogiltigt val!")
pass
Tillfälliga platshållare
if val == "1":
pass # Funktionen skrivs senare.
pass gör ingenting.
Det används här för att kodblocket inte ska vara tomt medan programmet byggs.
När funktionen är färdig ersätts pass med funktionsanropet.
Spara recept
Läs in användarens data
def lägga_till_recept():
namn = input("Vad heter receptet? ")
ingredienser = input(
"Ange ingredienser (komma-separerade): "
)
meddelande = f"{namn}|{ingredienser}\n"
meddelande får samma format som en rad i filen.
Append
Lägg receptet sist i filen
try:
with open(RECEPTFIL, "a") as fil:
fil.write(meddelande)
print("Recept sparat.")
except Exception as e:
print(f"Något gick fel: {e}")
"a" behåller tidigare recept och lägger till en ny rad.
Efter skrivning
Så förändras filen
Före:
Pannkakor|mjölk,ägg,mjöl
Efter:
Pannkakor|mjölk,ägg,mjöl
Tomatsoppa|tomater,lök,vatten
Läsa recept
Läs alla rader
def visa_recept():
try:
with open(RECEPTFIL, "r") as fil:
rader = fil.readlines()
except FileNotFoundError:
print("Filen med recept finns inte ännu.")
return
readlines() returnerar en lista med filens rader.
Tom fil
Kontrollera innan loopen
if not rader:
print("Inga recept hittades.")
return
En tom lista är falsk i ett villkor.
return avslutar funktionen när det inte finns något att visa.
Tolka raden
Dela upp receptets delar
for rad in rader:
namn, ingredienser = rad.strip().split("|")
strip()tar bort radbrytningen.split("|")delar texten i två delar.- Delarna sparas i
namnochingredienser.
Ingredienser
Dela en gång till
print(f"\nRecept: {namn}")
print("Ingredienser:")
for ingrediens in ingredienser.split(","):
print(f"- {ingrediens.strip()}")
split(",") skapar en lista med ingredienser.
Exempel
Från filrad till utskrift
Pannkakor|mjölk,ägg,mjöl
Recept: Pannkakor
Ingredienser:
- mjölk
- ägg
- mjöl
Färdig meny
Ersätt pass med funktionsanrop
if val == "1":
lägga_till_recept()
elif val == "2":
visa_recept()
elif val == "3":
print("Programmet avslutas.")
break
else:
print("Ogiltigt val, försök igen.")
Programstart
Starta main()
if __name__ == "__main__":
main()
Villkoret är sant när filen körs direkt.
Då startas huvudprogrammet.
Om filen importeras som en modul startar inte menyn automatiskt.
Programflöde
Så hänger delarna ihop
main()
Visar menyn och läser användarens val.
lägga_till_recept()
Skriver en ny rad med "a".
visa_recept()
Läser, delar upp och visar alla rader.
Testa
Viktiga testfall
- Lägg till ett recept i en tom fil.
- Lägg till flera recept.
- Visa recepten och kontrollera formateringen.
- Visa recept när filen saknas.
- Starta programmet igen och kontrollera att recepten finns kvar.
- Skriv ett ogiltigt menyval.
Sammanfattning
Miniuppgiften kombinerar...
- en meny med loop och villkor
- funktioner med tydliga ansvar
"a"för att spara utan att raderareadlines()för att läsa filens radersplit()för att tolka sparad texttry/exceptför förväntade filfel