Filer och kontexthanterare
Skriv lösningarna till uppgifterna i en och samma fil och testa koden själv innan du använder rättningsskriptet. Att kunna testa sin kod är en viktig del av att programmera!
Att lära dig från uppgifterna
- Förstå hur filer öppnas och stängs för läsning och skrivning.
- Förstå hur kontexthanterare fungerar för filhantering
Man kan få max 95 poäng och för att få godkänt krävs 55 poäng (85 för väl godkänt). Försök dock att lösa alla uppgifter då inte alla fel upptäcks av rättningsskriptet. Om ni har lite marginal kan ni kanske bli godkända även om assistenten som rättar hittar något sådant fel.
Förberedelser
Två datafiler behövs för att lösa många av uppgifterna nedan. Börja därför med att kopiera dem från kurskatalogen. Du hittar filerna firstnames
och lastnames
i katalogen /courses/729G46/kursmaterial/tema2
.
Filen firstnames
innehåller förnamn och filen lastnames
innehåller efternamn.
Filer att hämta via webben istället för via kurskatalogen:
Högerklicka på länkarna och välj “Spara som…”.
Kommentera ut testkörningar före rättningsskriptet
Rättningsskriptet är väldigt försiktigt av sig och kopierar er fil till en temporär katalog för att inte riskera att sabba något i er orginalkatalog om något skulle gå fel vid rättningen. Däremot flyttas inte datafilerna med eftersom det inte finns något sätt för rättningsskriptet att veta var ni lagt dem. (Flera möjligheter är naturliga, t.ex. i samma katalog som .py-filen, i en underkatalog till den kallad t.ex. ‘data’ eller ‘filer’ eller i en motsvarande katalog en nivå upp i filträdet.)
När er fil sedan läses in av rättningsskriptet så exekveras den och eventuella testanrop som ni har med körs. Försöker dessa testanrop läsa från filer som inte finns kommer det dock att krascha innan rättningsskriptet ens kan påbörja rättningen. Jag har grävt lite i möjligheten att inte exekvera hela filen för att köra rättningsskriptet, men tyvärr är Pythons modulsystem lite begränsat på det området och det skulle krävas en ganska omfattande revision av hur rättningsskriptet arbetar för att åstadkomma något sådant.
Alltså, innan ni kör rättningsskriptet på uppgifter som läser från filer, kommentera ut era testanrop, som illustrerat på rad 6 nedan:
|
|
Uppgift 8.1
Dessa tre första uppgifter introducerar funktionen open
och de mest grundläggande operationerna på filer. Börja med att läsa på om open
i Pythons officiella dokumentation: Funktionen open
. Man kan också läsa kapitel 11 i Skansholm som handlar om just arbete med textfiler.
Kontexthanterare (som skapas med with
) får inte användas i dessa uppgifter utan filerna skall öppnas och stängas manuellt. Vi kommer till kontexthanterare snart, men det är viktigt att känna till hur det fungerar utan dessa eftersom motsvarande funktionalitet inte finns i alla programmeringsspråk. Det finns också situationer, även i Python, där kontexthanterare inte är lämpliga.
8.1.1 (5p)
Skriv en funktion print_lines(path)
som tar sökvägen till en fil representerat som en sträng som argument. Funktionen skall skriva ut alla rader i filen.
Exempel
>>> print_lines('data/firstnames')
Marie
Ida
Maria
Sofia
Rosalind
Cecilia
Jocelyn
Dorothy
Ada
Barbara
Grace
Tips 1: Innan vi kan läsa från en fil, så måste vi öppna filen för läsning med hjälp av funktionen open()
. Ett filobjekt måste stängas när man är färdig med det.
Tips 2: När vi har öppnat en fil så läser vi den med hjälp av metoden readlines()
. Du kan läsa pythondokumentationen för readlines() översiktligt. Du behöver inte förstå allt, men du får exempel på den terminologi som används. Vissa termer kommer kanske att vara nya. Vad betyder t.ex. EOF?
8.1.2 (5p)
Skriv en funktion save_strings(path, lst)
som tar en sökväg och en lista av strängar och skriver strängarna till filen, en sträng per rad. Om filen redan existerar ska innehållet skrivas över. Använd file.write
för att skriva varje sträng i lst
till filen.
Tips: Kom ihåg att tecknet för en radbrytning är '\n'
.
Exempel
>>> save_strings('stringfile.txt', ['rad 1', 'rad 2', 'rad 3'])
>>> save_strings('stringfile.txt', ['rad 4', 'rad 5', 'rad 6'])
Innehållet i stringfile.txt
efter båda anropen ovan:
rad 4
rad 5
rad 6
8.1.3 (5p)
Skriv en funktion save_more_strings(path, lst)
som tar en sökväg och en lista av strängar och skriver strängarna till filen, en sträng per rad. Om filen redan existerar ska innehållet läggas till i slutet av filen. Använd file.writelines
för att skriva hela listan till filen i ett anrop.
Exempel
>>> save_strings('stringfile.txt', ['rad 1', 'rad 2', 'rad 3'])
>>> save_more_strings('stringfile.txt', ['rad 4', 'rad 5', 'rad 6'])
Innehållet i stringfile.txt
efter båda anropen ovan:
rad 1
rad 2
rad 3
rad 4
rad 5
rad 6
OBS! Kom ihåg att kommentera ut testanrop från er fil innan ni kör rättningsskriptet!
Kontexthanteraren with
Kontexthanterare är ett alternativt sätt att göra tilldelning i Python som ser till att mer komplexa operationer relaterade till tilldeningen, och eventuell uppstädning som behöver göras när variabeln inte längre behövs, sköts automatiskt. Du kan läsa mer om hur with
fungerar i Pythons officiella dokumentation: The with statement
Kontexthanterare är ett kraftfullt sätt att dölja onödig komplexitet (som att behöva stänga filer när man är färdig med dem) men vi kommer i den här kursen bara använda dem för att läsa från filer. Ni kommer fortfarande att behöva använda open
, men exakt hur kommer se lite annorlunda ut. Om ni inte lyckas reda ut hur det fungerar med hjälp av dokumentationen så kan ni googla på t.ex. “python with open examples” för att hitta exempel på hur det fungerar.
Uppgift 8.2 (5p)
Skriv en funktion count_lines(path)
som returnerar antalet rader i en fil.
Använd kontexthanteraren för filobjekt med hjälp av with
för att slippa att manuellt stänga filen.
Exempel
>>> count_lines('data/firstnames')
11
Uppgift 8.3 (5p)
Skriv funktionen get_names_starting_with(path, letter)
som läser in en fil med namn och returnerar en lista på alla namn som börjar på bokstaven letter
. Det ska inte spela någon roll ifall letter
är gemen eller versal.
Tips: För att slippa anropa str.strip
eller str.rstrip
på varje element för att bli av med radbrytningstecknen kan man istället för att läsa in raderna med file.readlines
använda file.read
, som returnerar hela filen som en sträng, och sedan strängmetoden str.splitlines
(eller str.split('\n')
), för att dela upp den strängen i rader. Sätter man ihop det blir det t.ex. f.read().splitlines()
.
Exempel
>>> get_names_starting_with('data/lastnames', 'C')
['Curie', 'Crowfoot Hodgkin']
>>> get_names_starting_with('data/firstnames', 'm')
['Marie', 'Maria']
OBS! Kom ihåg att kommentera ut testanrop från er fil innan ni kör rättningsskriptet!
Uppgift 8.4 (10p)
Skriv funktionen find_in_file(path, search_str)
som tar en sökväg och en söksträng. Filen skall returnera en tupel med två element, radnumret (numrerat från 1) på den första raden som innehåller search_str
samt en sträng som representerar hela raden (utom radbrytningen). Om ingen rad i filen innehåller search_str
skall funktionen returnera None
.
Exempel
>>> find_in_file('data/lastnames', 'Love')
(9, 'Lovelace')
Uppgift 8.5 (5p)
Skriv funktionen find_all_in_file(path, search_str)
som tar en sökväg och en söksträng. Filen skall returnera en lista av tupler med två element*. Varje tupel skall innehålla radnumret (numrerat från 1) på den rad som innehåller search_str
samt en sträng som representerar hela raden (utom radbrytningen). Om ingen rad i filen innehåller search_str
skall funktionen returnera en tom lista.
* Detta är ett exempel på en så kallad nästlad datastruktur. Vi kommer titta mer på dessa längre fram i kursen.
Exempel
>>> find_all_in_file('data/lastnames', 'Love')
[(9, 'Lovelace')]
>>> find_all_in_file('data/lastnames', 'el')
[(7, 'Bell Burnell'), (9, 'Lovelace')]
>>> find_all_in_file('data/lastnames', '-')
[(3, 'Goeppert-Mayer'), (6, 'Payne-Gaposchkin')]
Uppgift 8.6 (10p)
Skriv en funktion get_random_name(firstnames_path, lastnames_path)
som skapar slumpmässiga namn genom att använda för- och efternamn från de angivna filerna. För- och efternamn får inte komma från samma rad i båda filerna, dvs. det slumpade namnet skall inte vara ett av de riktiga namnen.
Tips: För att garanterat välja två olika slumpmässiga tal kan man använda random.sample(range(lowest, highest+1), k=2)
.
OBS! Kom ihåg att kommentera ut testanrop från er fil innan ni kör rättningsskriptet!
Uppgift 8.7 (10p)
Skriv funktionen combine_lines(input_path1, input_path2, sep)
som tar två sökvägar och en separator sep
som argument. Funktionen skall läsa de båda filerna (som alltså måste existera) och returnera en lista med strängar som skapats genom att slå ihop rad 1 (exklusive radbrytningar) från båda filerna med sep
mellan dem, rad 2 från båda filerna med sep
mellan, osv.
Exempel
>>> combine_lines('data/firstnames', 'data/lastnames', ' ')
['Marie Curie', 'Ida Noddack', 'Maria Goeppert-Mayer', 'Sofia Vasiljevna Kovalevskaja', 'Rosalind Franklin', 'Cecilia Payne-Gaposchkin', 'Jocelyn Bell Burnell', 'Dorothy Crowfoot Hodgkin', 'Ada Lovelace', 'Barbara McClintock', 'Grace Hopper']
>>> combine_lines('data/lastnames', 'data/firstnames', ' är efternamnet och förnamnet är ')
['Curie är efternamnet och förnamnet är Marie', 'Noddack är efternamnet och förnamnet är Ida', 'Goeppert-Mayer är efternamnet och förnamnet är Maria', 'Vasiljevna Kovalevskaja är efternamnet och förnamnet är Sofia', 'Franklin är efternamnet och förnamnet är Rosalind', 'Payne-Gaposchkin är efternamnet och förnamnet är Cecilia', 'Bell Burnell är efternamnet och förnamnet är Jocelyn', 'Crowfoot Hodgkin är efternamnet och förnamnet är Dorothy', 'Lovelace är efternamnet och förnamnet är Ada', 'McClintock är efternamnet och förnamnet är Barbara', 'Hopper är efternamnet och förnamnet är Grace']
Uppgift 8.8 (5p)
Skriv funktionen combine_and_save_names(firstnames_path, lastnames_path, output_path)
som tar tre sökvägar som argument. Funktionen skall läsa de båda första filerna (som förväntas innehålla förnamn respektive efternamn på personer) och skapa en lista med strängar genom att slå ihop rad 1 (exklusive radbrytningar) från båda filerna, rad 2 från båda filerna, osv. enligt mönstret nedan. Dessa strängar skall sedan skrivas till filen output_path
. Om filen output_path
redan existerar skall dess innehåll skrivas över.
Använd funktioner från uppgifterna ovan för att lösa problemet, det är inte tillåtet att anropa open
direkt från combine_and_save_names
.
Exempel
>>> combine_and_save_names('data/firstnames', 'data/lastnames', 'scientists.csv')
Innehåll i scientists.csv
efter att ha kört raden ovan:
Marie,Curie
Ida,Noddack
Maria,Goeppert-Mayer
Sofia,Vasiljevna Kovalevskaja
Rosalind,Franklin
Cecilia,Payne-Gaposchkin
Jocelyn,Bell Burnell
Dorothy,Crowfoot Hodgkin
Ada,Lovelace
Barbara,McClintock
Grace,Hopper
Uppgift 8.9 (10p)
Skriv funktionen get_first_name(path, lastname)
som tar in ett efternamn och en sökväg som argument. Sökvägen ska gå till en fil som matchar formatet i scientists.csv
, dvs en CSV-fil med förnamn i första kolumnen och efternamn i andra kolumnen. Funktionen ska returnera det första förnamnet på den första personen med det angivna efternamnet i den angivna filen. Använd scientists.csv
som exempel.
Tips: Strängmetoden str.split
är användbar för att separera för- och efternamn. Kom ihåg att efternamn kan ha mellanslag!
OBS! Kom ihåg att kommentera ut testanrop från er fil innan ni kör rättningsskriptet!
Uppgift 8.10 (5p)
Skriv en funktion get_names_of_length(path, length)
som returnerar en lista av alla namn av längden length
i en fil med sökvägen path
.
Uppgift 8.11 (5p)
Skriv en funktion get_longest_name(path)
som plockar fram det längsta namnet i den angivna filen.
Uppgift 8.12 (10p)
Skriv en funktion get_palindromes(path)
som returnerar en lista med alla namn som är palindrom från en given fil. Det räcker att ett namn är ett palindrom när alla bokstäver konverterats till gemener. (Ett palindrom är en sekvens tecken som blir samma oavsett om den läses framifrån eller bakifrån, t.ex. “kajak”, “apa” eller “tillit”.)
Exempel
>>> get_palindromes('data/firstnames')
['Ada']
OBS! Kom ihåg att kommentera ut testanrop från er fil innan ni kör rättningsskriptet!
Sidansvarig: Johan Falkenjack
Senast uppdaterad: 2025-08-05