Osa 6

Tiedostojen lukeminen

Yksi tavallinen ohjelmoinnin käyttötarkoitus on käsitellä tiedostoissa olevaa tietoa. Ohjelmat voivat lukea tietoa tiedostoista ja tallentaa tuloksia tiedostoihin. Tiedostojen avulla voimme käsitellä suuriakin aineistoja helposti automaattisesti.

Oletamme tällä kurssilla, että käsiteltävät tiedostot ovat tekstitiedostoja eli ne muodostuvat riveistä, joilla on tekstiä. Esimerkiksi kurssilla käytetty Visual Studio Code -editori käsittelee tekstitiedostoja. Huomaa, että esimerkiksi Word-dokumentti ei ole tekstitiedosto, vaan siinä on tekstin lisäksi muotoilutietoja ja sen käsittely ohjelmallisesti olisi vaikeaa.

Tiedostosta lukeminen

Käytetään esimerkkinä tiedostoa esimerkki.txt, jonka sisältönä on:

Esimerkkidata

Moi kaikki! Esimerkkitiedostomme on kolmerivinen. Viimeinen rivi.

Hyvä tapa käsitellä tiedostoja Pythonissa on käyttää with-lausetta, jonka alkurivi avaa tiedoston. Tämän jälkeen tulee lohko, jonka sisällä tiedostoa voi käsitellä. Lohkon jälkeen tiedosto sulkeutuu automaattisesti, eikä sitä voi enää käsitellä.

Esimerkiksi seuraava koodi lukee ja tulostaa tiedoston sisällön:

with open("esimerkki.txt") as tiedosto:
    sisalto = tiedosto.read()
    print(sisalto)
Esimerkkitulostus

Moi kaikki! Esimerkkitiedostomme on kolmerivinen. Viimeinen rivi.

Koodissa muuttuja tiedosto on tiedostokahva, jonka kautta tiedostoa voi käsitellä avaamisen jälkeen. Tässä tapauksessa käytämme metodia read, joka palauttaa koko tiedoston sisällön yhtenä merkkijonona. Tässä tapauksessa palautettu merkkijono on seuraava:

"Moi kaikki!\nEsimerkkitiedostomme on kolmerivinen.\nViimeinen rivi."

Tiedoston sisällön läpikäynti

Metodi read on näppärä, jos halutaan esimerkiksi tulostaa tiedoston sisältö kokonaisuudessaan ruudulle. Usein haluamme kuitenkin käsitellä tiedostoa rivi kerrallaan.

Voimme käyttää tiedoston sisällön lukemiseen for-silmukkaa, joka käy läpi tiedoston rivit yksi kerrallaan – siis samaan tapaan kuin esimerkiksi listan läpikäynnissä.

Seuraava esimerkki lukee saman tiedoston nyt käyttäen for-silmukkaa, poistaa joka rivin perästä rivinvaihdon ja laskee rivien yhteispituuden:

with open("esimerkki.txt") as tiedosto:
    laskuri = 0
    yhteispituus = 0

    for rivi in tiedosto:
        rivi = rivi.replace("\n", "")
        laskuri += 1
        print("Rivi", laskuri, rivi)
        pituus = len(rivi)
        yhteispituus += pituus

print("Rivien yhteispituus:", yhteispituus)
Esimerkkitulostus

Rivi 1 Moi kaikki! Rivi 2 Esimerkkitiedostomme on kolmerivinen. Rivi 3 Viimeinen rivi. Rivien yhteispituus: 63

Huomaa, että rivien läpikäynnissä jokaisen rivin perässä on rivinvaihto \n. Yllä oleva koodi kuitenkin poistaa rivinvaihdot replace-funktiolla, joka korvaa rivinvaihdot tyhjillä merkkijonoilla. Tämän ansiosta tulostukseen ei tule ylimääräisiä rivivaihtoja ja ohjelma laskee oikein tiedoston rivien yhteispituuden.

Ohjelmointitehtävä:

Suurin luku

Pisteitä:
Loading...

/

Loading...

Tiedostoon luvut.txt on tallennettu lukuja, yksi luku per rivi seuraavan esimerkin mukaisesti:

2
45
108
3
-10
1100
...jne...

Kirjoita funktio suurin, joka lukee tiedoston ja palauttaa suurimman tiedostosta löytyvän luvun.

Huomaa, että tiedoston nimi on aina luvut.txt eikä funktiolle anneta parametria.

Huom! Jos VS Code ei löydä tiedostoa vaikka olet tarkastanut tiedoston nimen kirjoitusasun, voit kokeilla seuraavaa heti tehtävän jälkeen olevaa ohjetta.

Mitä jos VS code ei löydä tiedostoja koodia suoritettaessa?

Jos VS Code ei löydä tiedostoa suorittaessasi koodia (vihreää nappia painamalla) vaikka olet tarkastanut tiedoston nimen kirjoitusasun, voit kokeilla seuraavaa:

  • Mene asetuksiin valikosta File -> Preferences -> Settings
  • Etsi muutettava kohta hakusanalla "executeinfile"
  • Valitse välilehti Workspace
  • Laita raksi kohtaan Python -> Terminal -> Execute In File Dir

Oikein tehtynä asetus näyttää suunilleen seuraavalta:

6 1 1

Jos edellinenkään ei toimi, voit kopioida kansiossa src olevan testaukseen käytetyn tiedoston sisällön

6 1 2

suoraan tehtäväkansion alle

6 1 3

Tiedostoja lukevan koodin debuggaus

Jos yrität käyttää VS Coden debuggeria tiedostoja lukevan koodin suorittamiseen, törmäät ikävään virheilmoitukseen:

6 1 4

Syynä tälle on se, että debuggeri etsii tiedostoja tehtäväkansion juuresta eikä edes Execute In File Dir -asetus ei asiaa muuta. Helpoin ratkaisu ongelmaan on edellisessä luvussa kuvattu testaukseen käytetyn tiedoston kopioiminen tehtävähakemiston juureen.

Kun olet kopioinut tiedostot tehtävähakemiston juureen, joudut ehkä vielä käynnistämään visual studio coden uudelleen jotta kaikki toimisi.

CSV-tiedoston lukeminen

CSV-tiedosto (Comma Separated Values) on tekstitiedosto, jonka jokaisella rivillä on tietyllä välimerkillä erotettua tietoa. Välimerkkinä on usein pilkku , tai puolipiste ;, mutta mikä tahansa muukin merkki on periaatteessa mahdollinen.

CSV-tiedostoja käytetään usein erilaisten aineistojen esittämiseen. Myös Excelin ja muiden vastaavien ohjelmien taulukot voidaan tallentaa CSV-muodossa, jolloin niitä on helppo käsitellä muilla ohjelmilla.

Voimme lukea CSV-tiedoston rivit for-silmukalla, mutta miten erottaa rivillä olevat tiedot toisistaan? Helppo tapa on käyttää merkkijonojen split-metodia: metodille annetaan haluttu välimerkki, ja se palauttaa tiedot välimerkin mukaan eroteltuna listana merkkijonoja.

Esimerkki metodin käytöstä:

teksti = "apina,banaani,cembalo"
sanat = teksti.split(",")
for sana in sanat:
    print(sana)
Esimerkkitulostus

apina banaani cembalo

Tarkastellaan esimerkkinä tiedostoa arvosanat.csv, joka sisältää jokaisella rivillä aluksi opiskelijan nimen ja sen jälkeen tämän eri kursseista saamat arvosanat. Tiedot on erotettu toisistaan puolipisteillä.

Esimerkkidata

Pekka;5;4;5;3;4;5;5;4;2;4 Paula;3;4;2;4;4;2;3;1;3;3 Pirjo;4;5;5;4;5;5;4;5;4;4

Seuraava ohjelma käy läpi tiedoston rivit, jakaa jokaisen rivin osiin ja näyttää opiskelijan nimen sekä arvosanat.

with open("arvosanat.csv") as tiedosto:
    for rivi in tiedosto:
        rivi = rivi.replace("\n", "")
        osat = rivi.split(";")
        nimi = osat[0]
        arvosanat = osat[1:]
        print("Nimi:", nimi)
        print("Arvosanat:", arvosanat)
Esimerkkitulostus

Nimi: Pekka Arvosanat: ['5', '4', '5', '3', '4', '5', '5', '4', '2', '4'] Nimi: Paula Arvosanat: ['3', '4', '2', '4', '4', '2', '3', '1', '3', '3'] Nimi: Pirjo Arvosanat: ['4', '5', '5', '4', '5', '5', '4', '5', '4', '4']

Ohjelmointitehtävä:

Hedelmäkauppa

Pisteitä:
Loading...

/

Loading...

Tiedostossa hedelmat.csv on hedelmiä hintoineen seuraavan esimerkin mukaisesti:

banaani;6.50
omena;4.95
appelsiini;8.0
...jne...

Kirjoita funktio lue_hedelmat, joka lukee hedelmätiedoston ja muodostaa siitä sanakirjan, jossa hedelmän nimi on avain ja hinta arvo. Hinnan tulee olla float-arvona sanakirjassa.

Huomaa, että tiedoston nimi on aina hedelmat.csv eikä funktiolle anneta parametria.

Lopuksi funktio palauttaa tämän sanakirjan.

Huom! Jos VS Code ei löydä tiedostoa vaikka olet tarkastanut tiedoston nimen kirjoitusasun, voit kokeilla täällä olevaa ohjetta.

Ohjelmointitehtävä:

Matriisi

Pisteitä:
Loading...

/

Loading...

Tiedostossa matriisi.txt on seuraavan esimerkin kaltainen matriisi:

1,0,2,8,2,1,3,2,5,2,2,2
9,2,4,5,2,4,2,4,1,10,4,2
...jne...

Kirjoita funktiot summa ja maksimi, jotka lukevat ja palauttavat nimensä mukaisesti matriisin kaikkien alkioiden summan ja suurimman alkion.

Kirjoita lisäksi funktio rivisummat, joka palauttaa listassa kaikkien matriisin rivien summat. Esimerkiksi matriisille

1,2,3
2,3,4

funktio palauttaisi listan [6, 9].

Vinkki: Voit kirjoittaa ohjelmaan myös muita funktioita – kannattaa siis miettiä, mitä kaikkia yhteisiä toimintoja kolmea funktiota varten vaaditaan. Huomaa, että tiedoston nimi on aina matriisi.txt eikä tehtävänannossa määritellyille funktioille anneta parametreja. Itse lisäämäsi funktiot voivat hyödyntää myös parametreja.

Huom! Jos VS Code ei löydä tiedostoa vaikka olet tarkastanut tiedoston nimen kirjoitusasun, voit kokeilla täällä olevaa ohjetta.

Saman tiedoston lukeminen moneen kertaan

Joissain tilanteissa ohjelman on tarvetta lukea sama tiedosto useampaan kertaan. Tarkastellaan esimerkkinä seuraavaa ohjelmaa, joka käsittelee henkilötietoja sisältävää tiedostoa:

Esimerkkidata
Pekka;40;Helsinki Emilia;34;Espoo Erkki;42;Turku Antti;100;Helsinki Liisa;58;Suonenjoki
with open("henkilot.csv") as tiedosto:
    # tulostetaan nimet
    for rivi in tiedosto:
        osat = rivi.split(";")
        print("Nimi:", osat[0])

    # etsitään vanhin
    vanhimman_ika = -1
    for rivi in tiedosto:
        osat = rivi.split(";")
        nimi = osat[0]
        ika = int(osat[1])
        if ika > vanhimman_ika:
            vanhimman_ika = ika
            vanhin = nimi
    print("vanhin on", vanhin)

Ohjelma aiheuttaa erikoisen virheilmoituksen:

Traceback (most recent call last):
    print("vanhin on"; vanhin)
UnboundLocalError: local variable 'vanhin' referenced before assignment

Syynä virheelle on se, että jälkimmäistä for-silmukkaa ei suoriteta ollenkaan, sillä tiedoston voi lukea vain kerran. Tämän jälkeen ollaan päästy "tiedoston loppuun", ja vaikka yritetään lukea tiedostosta lisää jälkimmäisessä silmukassa, tietoon ei päästä enää käsiksi.

Tiedosto onkin avattava uudelleen komennolla open toista lukukertaa varten:

with open("henkilot.csv") as tiedosto:
    # tulostetaan nimet
    for rivi in tiedosto:
        osat = rivi.split(";")
        print("Nimi:", osat[0])

with open("henkilot.csv") as tiedosto:
    # etsitään vanhin
    vanhimman_ika = -1
    for rivi in tiedosto:
        osat = rivi.split(";")
        nimi = osat[0]
        ika = int(osat[1])
        if ika > vanhimman_ika:
            vanhimman_ika = ika
            vanhin = nimi
    print("vanhin on", vanhin)

Yleensä aina on kuitenkin parasta lukea tiedosto vain kerran ja tallentaa se muotoon, jota ohjelman toiminnallisuudet pystyvät hyödyntämään:

henkilot = []
# luetaan tiedostosta henkilöt listaan
with open("henkilot.csv") as tiedosto:
    for rivi in tiedosto:
        osat = rivi.split(";")
        henkilot.append((osat[0], int(osat[1]), osat[2]))

# tulostetaan nimet
for henkilo in henkilot:
    print("Nimi:", henkilo[0])

# etsitään vanhin
vanhimman_ika = -1
for henkilo in henkilot:
    nimi = henkilo[0]
    ika = henkilo[1]
    if ika > vanhimman_ika:
        vanhimman_ika = ika
        vanhin = nimi
print("vanhin on", vanhin)

Lisää CSV-tiedoston käsittelyä

Jatketaan opiskelijoiden arvosanoja sisältävän tiedoston arvosanat.csv käsittelyä. Tiedosto näyttää siis seuraavalta:

Esimerkkidata

Pekka;5;4;5;3;4;5;5;4;2;4 Paula;3;4;2;4;4;2;3;1;3;3 Pirjo;4;5;5;4;5;5;4;5;4;4

Seuraava ohjelma luo tiedoston perusteella sanakirjan arvosanat, jossa jokainen avain on opiskelijan nimi ja vastaava arvo on lista arvosanoista. Ohjelma muuttaa arvosanat kokonaisluvuiksi, jotta niitä on mukavampaa käsitellä myöhemmin.

arvosanat = {}
with open("arvosanat.csv") as tiedosto:
    for rivi in tiedosto:
        rivi = rivi.replace("\n", "")
        osat = rivi.split(";")
        nimi = osat[0]
        arvosanat[nimi] = []
        for arvosana in osat[1:]:
            arvosanat[nimi].append(int(arvosana))

print(arvosanat)
Esimerkkitulostus

{'Pekka': [5, 4, 5, 3, 4, 5, 5, 4, 2, 4], 'Paula': [3, 4, 2, 4, 4, 2, 3, 1, 3, 3], 'Pirjo': [4, 5, 5, 4, 5, 5, 4, 5, 4, 4]}

Tämän jälkeen voimme vaikkapa tulostaa analyysin arvosanoista käymällä läpi sanakirjan arvosanat perusteella:

for nimi, lista in arvosanat.items():
    paras = max(lista)
    keskiarvo = sum(lista) / len(lista)
    print(f"{nimi}: paras arvosana {paras}, keskiarvo {keskiarvo:.2f}")
Esimerkkitulostus

Pekka: paras arvosana 5, keskiarvo 4.10 Paula: paras arvosana 4, keskiarvo 2.90 Pirjo: paras arvosana 5, keskiarvo 4.50

Kannattaa tutustua huolella esimerkkikoodiin. Se voi ensisilmäyksellä vaikuttaa monimutkaiselta, mutta ratkaisu on helposti sovellettavissa monenlaisiin datatiedostoihin.

Eroon turhista riveistä, välilyönneistä ja rivinvaihdoista

Olemme tallentaneet Excelistä nimiä taulukon CSV-muodossa:

etunimi; sukunimi
Pekka; Python
Jaana; Java
Heikki; Haskell

Kuten tyypillistä, Excel on lisännyt sarakkeiden väliin erottimena toimivan puolipisteen lisäksi myös välilyönnin.

Haluamme tulostaa listalla olevat sukunimet. Koska ensimmäinen rivi kertoo sarakkeiden otsikot, ohitamme sen:

sukunimet = []
with open("henkilot.csv") as tiedosto:
    for rivi in tiedosto:
        osat = rivi.split(";")
        # ohitetaan otsikkorivi
        if osat[0] == "etunimi":
            continue
        sukunimet.append(osat[1])

print(sukunimet)

Tulostus näyttää seuraavalta:

Esimerkkitulostus

[' Python\n', ' Java\n', ' Haskell']

Kaikkiin paitsi viimeiseen rivin sukunimeen on jäänyt mukaan rivinvaihtomerkki, ja jokaisen sukunimen alkuun on jäänyt ikävä välilyönti.

Pääsisimme näistä eroon aiempien esimerkkien tapaan käyttämällä metodia replace, mutta parempi vaihtoehto tässä tilanteessa on käyttää metodia strip, joka poistaa merkkijonon alusta ja lopusta ns. whitespace-merkit, eli välilyönnit, rivinvaihdot ja muut normaalina merkkinä tulostumattomat merkit.

Kokeillaan metodin toimintaa konsolissa:

>>> " koe ".strip()
'koe'
>>> "\n\ntesti\n".strip()
'testi'
>>>

Tarvittava muutos ohjelmaan on helppo:

sukunimet = []
with open("henkilot.csv") as tiedosto:
    for rivi in tiedosto:
        osat = rivi.split(';')
        if osat[0] == "etunimi":
            continue # tämä oli otsikkorivi, ei huomioida!
        sukunimet.append(osat[1].strip())
print(sukunimet)

Tämän jälkeen tulostus on halutunlainen:

Esimerkkitulostus

['Python', 'Java', 'Haskell']

Merkkijonoilla on myös metodit lstrip ja rstrip, jotka poistavat ainoastaan merkkijonon vasemmalla tai oikealla puolella olevia merkkejä.

>>> " testimerkkijono  ".rstrip()
' testimerkkijono'
>>> " testimerkkijono  ".lstrip()
'testimerkkijono  '

Eri tiedostoissa olevien tietojen yhdistely

On hyvin yleistä, että ohjelmassa tarvittava data on talletettu useaan erilliseen tiedostoon. Tarkastellaan esimerkkinä tilannetta, jossa yrityksen henkilöstön tiedot ovat omassa tiedostossaan tyontekijat.csv:

hetu;nimi;osoite;kaupunki
080488-123X;Pekka Mikkola;Vilppulantie 7;00700 Helsinki
290274-044S;Liisa Marttinen;Mannerheimintie 100 A 10;00100 Helsinki
010479-007Z;Arto Vihavainen;Pihapolku 4;01010 Kerava
010499-345K;Leevi Hellas;Tapiolantie 11 B;02000 Espoo

Työntekijöiden palkat taas ovat talletettu omaan tiedostoonsa palkat.csv

hetu;palkka;bonus
080488-123X;3300;0
290274-044S;4150;200
010479-007Z;1300;1200

Molempien tiedostojen riveillä on ensin henkilötunnus, joka kertoo kenen tiedoista on kyse. Käyttämällä henkilötunnusta yhdistävänä tekijänä, on helppo yhdistää henkilöiden nimet ja palkat toisiinsa, ja tehdä esimerkiksi ohjelma, joka tulostaa seuraavanlaisen näkymän henkilöiden ansioihin:

Esimerkkitulostus
ansiot:
Pekka Mikkola    3300 euroa
Liisa Marttinen  4350 euroa
Arto Vihavainen  2500 euroa

Ohjelma käyttää aputietorakenteena kahta saankirjaa nimet ja palkat, joissa molemmissa avaimena toimii henkilötunnus:

nimet = {}

with open("tyontekijat.csv") as tiedosto:
    for rivi in tiedosto:
        osat = rivi.split(';')
        if osat[0] == "hetu":
            continue
        nimet[osat[0]] = osat[1]

palkat = {}

with open("palkat.csv") as tiedosto:
    for rivi in tiedosto:
        osat = rivi.split(';')
        if osat[0] == "hetu":
            continue
        palkat[osat[0]] = int(osat[1]) +int(osat[2])

print("ansiot:")

for hetu, nimi in nimet.items():
    if hetu in palkat:
        palkka = palkat[hetu]
        print(f"{nimi:16} {palkka} euroa")
    else:
        print(f"{nimi:16} 0 euroa")

Ohjelma siis muodostaa ensin sanakirjat nimet ja palkat, joiden sisältö näyttää seuraavilta:

{
    '080488-123X': 'Pekka Mikkola',
    '290274-044S': 'Liisa Marttinen',
    '010479-007Z': 'Arto Vihavainen',
    '010499-345K': 'Leevi Hellas'
}

{
    '080488-123X': 3300,
    '290274-044S': 4350,
    '010479-007Z': 2500
}

Lopun for-silmukka yhdistää henkilöiden nimet ja niitä vastaavat palkat sanakirjojen avulla.

Ohjelma huomioi myös tilanteen, jossa henkilön palkkatietoja ei ole olemassa.

Huomaa, että koska ohjelma käyttää aputietorakenteena sanakirjaa, ei henkilöitä vastaavien rivien järjestyksellä ole merkitystä.

Ohjelmointitehtävä:

Kurssin tulokset, osa 1

Pisteitä:
Loading...

/

Loading...

Ohjelma käsittelee kahta CSV-muotoista tiedostoa. Toisessa on tieto opiskelijoista:

opnro;etunimi;sukunimi
12345678;pekka;peloton
12345687;jaana;javanainen
12345699;liisa;virtanen

ja toisessa opiskelijoiden viikoittaisesta tehtävien lukumäärästä:

opnro;v1;v2;v3;v4;v5;v6;v7
12345678;4;1;1;4;5;2;4
12345687;3;5;3;1;5;4;6
12345699;10;2;2;7;10;2;2

Molempien CSV-tiedostojen ensimmäinen rivi on otsikkorivi, joka kertoo kunkin kentän sisällön.

Tee ohjelma, joka kysyy tiedostojen nimet ja tämän jälkeen tulostaa kunkin opiskelijan tehtävien yhteenlasketun määrän. Ohjelma toimii seuraavasti, kun tiedostojen sisältö on yllä oleva:

Esimerkkitulostus

Opiskelijatiedot: opiskelijat1.csv Tehtävätiedot: tehtavat1.csv pekka peloton 21 jaana javanainen 27 liisa virtanen 35

Vinkki: Ohjelman testaileminen on toivottoman hidasta, jos käyttäjä joutuu kirjoittamaan syötteen aina käsin. Testausvaiheessa syötteet kannattaakin antaa "kovakoodaamalla" ne esim. seuraavasti:

if False:
    # tänne ei tulla
    opiskelijatiedot = input("Opiskelijatiedot: ")
    tehtavatiedot = input("Tehtävätiedot: ")
else:
    # kovakoodatut syötteet
    opiskelijatiedot = "opiskelijat1.csv"
    tehtavatiedot = "tehtavat1.csv"

Ohjelman varsinainen toiminnallisuus on nyt "piilotettu" ehdon False-haaraan, jota ei suoriteta koskaan.

Jos taas halutaan nopeasti tarkastaa, toimiiko ohjelma myös käyttäjän kirjoittaessa syötteen, voidaan arvo False muuttaa arvoksi True:


if True:
    opiskelijatiedot = input("Opiskelijatiedot: ")
    tehtavatiedot = input("Tehtävätiedot: ")
else:
    # tänne ei tulla!
    opiskelijatiedot = "opiskelijat1.csv"
    tehtavatiedot = "tehtavat1.csv"

Kun koodi on kunnossa, voi ehtorakenteen poistaa.

Huom: tässä tehtävässä (eikä missään muussakaan tehtävissä missä ei erikseen pyydetä funktioiden toteuttamista) mitään koodia ei tule sijoittaa if __name__ == "__main__"-lohkoon!

Toinen huomio Jos VS Code ei löydä tiedostoa vaikka olet tarkastanut tiedoston nimen kirjoitusasun, voit täällä kokeilla olevaa ohjetta.

Ohjelmointitehtävä:

Kurssin tulokset, osa 2

Pisteitä:
Loading...

/

Loading...

Edellinen tehtävä laajenee vielä siten, että myös opiskelijan koepisteet luetaan CSV-tiedostosta. Tiedoston sisältö näyttää seuraavalta:

opnro;k1;k2;k3
12345678;4;1;4
12345687;3;5;3
12345699;10;2;2

Esimerkiksi opiskelija jonka opiskelijanumero on 12345678 on saanut kokeesta 4+1+4 eli yhteensä 9 pistettä.

Ohjelma kysyy tiedostojen nimet ja tulostaa jokaisen opiskelijan arvosanan:

Esimerkkitulostus

Opiskelijatiedot: opiskelijat1.csv Tehtävätiedot: tehtavat1.csv Koepisteet: koepisteet1.csv pekka peloton 0 jaana javanainen 1 liisa virtanen 3

Tehtyjen harjoitustehtävien määrästä saa pisteitä siten, että vähintään 10 % tehtävämäärästä tuo 1 pisteen, vähintään 20% tuo 2 pistettä jne., ja 100 % eli 40 harjoitustehtävää tuo 10 pistettä. Harjoitustehtävistä saatava pistemäärä on kokonaisluku.

Kurssin arvosana määräytyy kokeen ja harjoituspisteiden summan perusteella seuraavan taulukon mukaan:

kokeen pisteet + harjoitusten pisteetarvosana
0-140 (eli hylätty)
15-171
18-202
21-233
24-274
28-5

Huom: tässä tehtävässä (eikä missään muussakaan tehtävissä missä ei erikseen pyydetä funktioiden toteuttamista) mitään koodia ei tule sijoittaa if __name__ == "__main__"-lohkoon!

Ohjelmointitehtävä:

Kurssin tulokset, osa 3

Pisteitä:
Loading...

/

Loading...

Tässä tehtävässä muotoillaan edellisen tehtävän tulostus parempaan muotoon:

Esimerkkitulostus

Opiskelijatiedot: opiskelijat1.csv Tehtävätiedot: tehtavat1.csv Koepisteet: koepisteet1.csv

nimi                          teht_lkm  teht_pist koe_pist  yht_pist  arvosana
pekka peloton                 21        5         9         14        0
jaana javanainen              27        6         11        17        1
liisa virtanen                35        8         14        22        3

Jokaisella rivillä siis tulostetaan opiskelijan tehtävien lukumäärä, tehtävistä saatavat pisteet, kokeen pisteet, yhteispisteet (koe+harjoitukset) sekä arvosana "siististi" siten, että tulostus on jaoteltu sarakkeisiin. Nimisarakkeen leveys on 30 merkkiä ja muiden sarakkeiden leveys on tasan 10 merkkiä.

Tehtävässä kannattaa käyttää osassa 4 käsiteltyjä f-merkkijonoja.

Kannattaa huomata, että merkkijonojen ja lukujen tulostaminen noudattaa hieman erilaista logiikkaa f-merkkijonoissa:

sana = "python"
print(f"{sana:10}jatkuu")
print(f"{sana:>10}jatkuu")
Esimerkkitulostus
python    jatkuu
    pythonjatkuu

Oletusarvoisesti siis merkkijono sisentyy määritellyn levyisen alueen vasempaan reunaan. Merkillä >voidaan ohjata tulostus sisentymään oikeaan reunaan.

Lukuja tulostettaessa logiikka on päinvastainen

luku = 42
print(f"{luku:10}jatkuu")
print(f"{luku:<10}jatkuu")
Esimerkkitulostus
        42jatkuu
42        jatkuu

Oletusarvo lukujen yhteydessä on tulostuksen sisentyminen oikeaan reunaan. Merkillä < voidaan ohjata luvun tulostus sisentymään vasempaan reunaan.

Huom: tässä tehtävässä (eikä missään muussakaan tehtävissä missä ei erikseen pyydetä funktioiden toteuttamista) mitään koodia ei tule sijoittaa if __name__ == "__main__"-lohkoon!

Ohjelmointitehtävä:

Spell checker

Pisteitä:
Loading...

/

Loading...

Tee ohjelma, joka pyytää käyttäjää kirjoittamaan rivin englanninkielistä tekstiä. Ohjelma suorittaa tekstille oikeinkirjoitustarkistuksen ja tulostaa saman tekstin siten, että kaikki väärin kirjoitetut sanat on ympäröity tähdillä. Seuraavassa kaksi käyttöesimerkkiä:

Esimerkkitulostus

Write text: We use ptython to make a spell checker

We use *ptython* to make a spell checker
Esimerkkitulostus

Write text: This is acually a good and usefull program

This is *acually* good and *usefull* program

Kirjainten koolla ei ole merkitystä ohjelman toiminnan kannalta.

Ohjelma tunnistaa oikein kirjoitetut sanat käyttämällä tehtäväpohjassa olevaa tiedostoa wordlist.txt.

Huom: tässä tehtävässä (eikä missään muussakaan tehtävissä missä ei erikseen pyydetä funktioiden toteuttamista) mitään koodia ei tule sijoittaa if __name__ == "__main__"-lohkoon!

Toinen huomio Jos VS Code ei löydä tiedostoa vaikka olet tarkastanut tiedoston nimen kirjoitusasun, voit täällä kokeilla olevaa ohjetta.

Ohjelmointitehtävä:

Reseptihaku

Pisteitä:
Loading...

/

Loading...

Tässä tehtävässä tehdään ohjelma, joka tarjoaa käyttäjälle mahdollisuuden reseptien hakuun reseptin nimen, valmistusajan tai raaka-aineen nimen perusteella. Ohjelma lukee reseptit käyttäjän antamasta tiedostosta.

Jokainen resepti koostuu kolmesta tai useammasta rivistä reseptitiedostossa. Ensimmäisellä rivillä on reseptin nimi, toisella rivillä reseptin valmistusaika (kokonaisluku), ja kolmas ja sitä seuraavat rivit kertovat reseptin raaka-aineet. Reseptin raaka-aineiden kuvaus päättyy tyhjään riviin, poislukien viimeinen resepti. Tiedostossa voi olla useampia reseptejä. Alla kuvattuna esimerkkitiedosto.

Lettutaikina
15
maito
kananmuna
jauho
sokeri
suola
voi

Lihapullat
45
jauheliha
kananmuna
korppujauho

Tofurullat
30
tofu
riisi
vesi
porkkana
kurkku
avokado
wasabi

Pullataikina
60
maito
hiiva
kananmuna
suola
sokeri
kardemumma
voi

Vihje tässä tehtävässä lienee järkevintä lukea ensin tiedoston rivit listalle ja käsitellä sitten tätä listaa tehtävän edellyttämällä tavalla.

reseptien haku nimen perusteella

Tee funktio hae_nimi(tiedosto: str, sana: str) joka hakee parametrina annetun nimisestä tiedostosta reseptit, joiden nimessä esiintyy toisena parametrina annettu merkkijono. Funktio palauttaa listan, jossa kutakin löydettyä reseptiä vastaa merkkijono, joka kertoo reseptin nimen.

Esimerkki funktion käytöstä:

loydetyt = hae_nimi("reseptit1.txt", "pulla")

for resepti in loydetyt:
    print(resepti)
Esimerkkitulostus

Lihapullat Pullataikina

Huomaa, että hakusanojen kirjainten koolla ei ole merkitystä, eli hakusana pulla löytää myös reseptin Pullataikina, joka alkaa isolla kirjaimella.

Huom! Jos VS Code ei löydä tiedostoa vaikka olet tarkastanut tiedoston nimen kirjoitusasun, voit täällä kokeilla olevaa ohjetta.

reseptien hakeminen valmistusajan perusteella

Tee funktio hae_aika(tiedosto: str, aika: int) joka hakee parametrina annetun nimisestä tiedostosta reseptit, joiden valmistusaika on korkeintaan parametrina kerrottu minuuttimäärä.

Kriteerin täyttävät reseptit palautetaan edellisen tehtävän tapaan listana, nyt kerrotaan myös reseptin valmistumisaika. Esimerkki funktion käytöstä:

loydetyt = hae_aika("reseptit1.txt", 20)

for resepti in loydetyt:
    print(resepti)
Esimerkkitulostus

Lettutaikina, valmistusaika 15 min

reseptien hakeminen raaka-aineen perusteella

Varoitus tämä osa on edellisiä selvästi haastavampi. Jos tehtävä ei lähde heti aukenemaan, kannattanee tehdä ensin osan muut tehtävät ja palata lopuksi takaisin tähän. Huomaa, että voit lähettää moniosaisessa tehtävässä palvelimelle myös yksittäiset osat

Tee funktio hae_raakaaine(tiedosto: str, aine: str) joka hakee parametrina annetun nimisestä tiedostosta reseptit, jotka sisältävät toisena parametrina annetun raaka-aineen.

Kriteerin täyttävät reseptit palautetaan edellisen tehtävän tapaan listana. Esimerkki funktion käytöstä:

loydetyt = hae_raakaaine("reseptit1.txt", "maito")

for resepti in loydetyt:
    print(resepti)
Esimerkkitulostus

Lettutaikina, valmistusaika 15 min Pullataikina, valmistusaika 60 min

Ohjelmointitehtävä:

Kaupunkipyörät

Pisteitä:
Loading...

/

Loading...

Tässä tehtävässä tehdään muutama funktio, joiden avulla voidaan tarkastella kaupunkipyörien asemien sijaintia sisältävää tiedostoa.

Tiedostot näyttävät seuraavilta:

Longitude;Latitude;FID;name;total_slot;operative;id
24.950292890004903;60.155444793742276;1;Kaivopuisto;30;Yes;001
24.956347471358754;60.160959093887129;2;Laivasillankatu;12;Yes;002
24.944927399779715;60.158189199971673;3;Kapteeninpuistikko;16;Yes;003

Kutakin asemaa kohti tiedostossa on yksi rivi, joka kertoo aseman koordinaatit, aseman nimen ja muuta tunnistetietoa.

asemien välinen etäisyys

Tee ensin funktio hae_asematiedot(tiedosto: str), joka lukee asematiedot tiedostosta ja palauttaa ne sanakirjana, joka näyttää tältä:

Esimerkkitulostus
{
  "Kaivopuisto: (24.950292890004903, 60.155444793742276),
  "Laivasillankatu: (24.956347471358754, 60.160959093887129),
  "Kapteeninpuistikko: (24.944927399779715, 60.158189199971673)
}

Eli sanakirjan avaimena on aseman nimi ja arvona tuple, joka koostuu aseman koordinaateista, ensimmäisenä Longitude ja toisena Latitude.

Tee seuraavaksi funktio etaisyys(asemat: dict, asema1: str, asema2: str), joka palauttaa parametrina kerrottujen asemien välisen etäisyyden.

Etäisyys lasketaan seuraavalla kaavalla (hyödyntäen Pythagoraan lausetta):

# tämä rivi tarvitaan, jotta saadaan käyttöön metodi sqrt
import math

x_kilometreina = (longitude1 - longitude2) * 55.26
y_kilometreina = (latitude1 - latitude2) * 111.2
etaisyys = math.sqrt(x_kilometreina**2 + y_kilometreina**2)

Esimerkkisuorituksia:

asemat = hae_asematiedot('stations1.csv')
e = etaisyys(asemat, "Designmuseo", "Hietalahdentori")
print(e)
e = etaisyys(asemat, "Viiskulma", "Kaivopuisto")
print(e)
Esimerkkitulostus

0.9032737292463177 0.7753594392019532

Huom! Jos VS Code ei löydä tiedostoa vaikka olet tarkastanut tiedoston nimen kirjoitusasun, voit täällä kokeilla olevaa ohjetta.

pisin välimatka

Tee funktio suurin_etaisyys(asemat: dict), joka selvittää, mitkä kaksi asemaa ovat kauimpana toisistaan. Funktio palauttaa tuplen, jonka ensimmäiset kaksi arvoa kertovat asemien nimet ja kolmas arvo niiden välisen etäisyyden.

asemat = hae_asematiedot('stations1.csv')
asema1, asema2, suurin = suurin_etaisyys(asemat)
print(asema1, asema2, suurin)
Esimerkkitulostus

Laivasillankatu Hietalahdentori 1.478708873076181

Kysely:
Loading...
Pisteitä:
Loading...

Kirjaudu sisään nähdäksesi tehtävän