Arto Wikla 2011. Materiaalia saa vapaasti käyttää itseopiskeluun. Muu käyttö vaatii luvan.

Ohjelmoinnin jatkokurssi: harjoitukset s2011: 5/6 (28.11.-2.12.)

(Muutettu viimeksi 2.12.2011, sivu perustettu 1.12.2011.)

Sivun sisältö voi vielä muuttua!

Nämä harjoitukset liittyvät oppimateriaalin lukuihin 8 – 11.

Kaikki harjoitustehtävät on syytä tehdä. Jotkin tehtävät on merkitty keltaisella värillä. Ne ovat ehkä hieman muita haastavampia. Ilman niitäkin harjoituksista voi saada maksimipisteet, mutta ne lasketaan silti mukaan harjoituspisteitä määrättäessä – ne voivat siis korvata joitakin haasteettomampia tehtäviä tms. Mutta ennen kaikkea noista keltaisista tehtävistä sitä vasta oppiikin!

Huom:

Paljon iloa Comparable-rajapintaluokan totteuttamisesta

MyString-olioita automaattisesti järjestykseen

Ensimmäisen viikon tehtävässä 4.4 ohjelmoitiin MyString-luokkaan MyString-olioiden järjestyksen määräävä metodi

public int compareTo(MyString verrattava)

Kerro kääntäjällekin, että MyString-olioille on totetutettu tällainen vertailtavuus. Kertominen tapahtuu ilmoittamalla luokan otsikossa, että luokka toteuttaaa rajapintaluokan Comparable<MyString>.

Tämän jälkeen kääntäjä sallii MyString-olioita käsiteltävän yleiskäyttöisillä työkaluilla, jotka on ohjelmoitu osaamaan käyttää kaikkia olioita, joiden luokka toteuttaa rajapintaluokan Comparable. (Vrt: Juustotehdas osaa lypsää kaikkia eläimiä, joiden luokka totetuttaa rajapintaluokan Lypsava.)

Java-APIn Collections-luokka tarjoaa koko joukon yleiskäyttöisiä kirjastometodeita (eli julkisia luokkametodeita), joilla mitä tahansa järjestyksen omaavien olioiden kokoelmia voidaan käsitellä.

"Omata järjestys" siis tarkoittaa juuri sitä, että olion luokka toteuttaa rajapintaluokan Comparable.

Collections-luokka löytyy pakkauksesta java.util.

Huomaa, että rajapintaluokka Collection on ihan eri asia kuin luokka Collections, etsi siis juuri tuo jälkimmäinen! Älä myöskään häkelly metodien kummallisista otsikoista – "kummallisuudet" liittyvät Javan ns. geneerisiin tyyppeihin.

Kokoelmiksi (Collection) Javassa kutsutaan monia erilaisia valmiita tietojoukkojen esittämiseen laadittuja luokkia. Tuttu esimerkki on ArrayList. Juuri tällaisten kokoelmaolioiden käsittelyyn Collections-luokka välineitä tarjoaa. Esimerkkejä kirjaston välineistä: binarySearch, max, min, sort.

Kokeile seuraavaa:

import java.util.Collections;
...

ArrayList<MyString> nimet = new ArrayList<MyString>();

MyString beepeli = new MyString("Beepeli");
MyString yypeli  = new MyString("Yypeli");
MyString aapeli  = new MyString("Aapeli");

nimet.add(beepeli);
nimet.add(yypeli);
nimet.add(aapeli);

System.out.println("Nimet alkuperäisessä järjestyksessä:");
for(MyString nimi: nimet) {
  System.out.println(nimi);
}
      
Collections.sort(nimet);

System.out.println("Nimet järjestyksessä:");      
for(MyString nimi: nimet) {              
  System.out.println(nimi);                                                     
}

Tulostuksen pitäisi näyttää seuraavalta:

Nimet alkuperäisessä järjestyksessä:
Beepeli
Yypeli
Aapeli
Nimet järjestyksessä:
Aapeli
Beepeli
Yypeli

Tässä tehtävässä on paljon luettavaa (ja toivottavasti ymmärrettävää!) ja varsin vähän ohjelmointia...

Vanha peruskurssin koetehtävä

Aika usein Ohjelmoinnin perusteet -kurssin kokeessa on ollut seuraavanlainen tehtävä:

Toteuta seuraava arvauspeli vuorovaikutteisena eli keskustelevana ohjelmana: Aamuisin ohjelmalle syötetään jokin määrä merkkijonoja missä järjestyksessä tahansa; olkoot nämä merkkijonot onnensanoja. Sama onnensana saa esiintyä useamminkin kuin kerran. Onnensanojen syötön päättää sana "loppu", joka ei itse ole onnensana.

Päivän mittaan pelaajat käyvät sitten arvaamassa sanoja. Jos pelaaja onnistuu arvaamaan jonkin onnensanan, ohjelma onnittelee pelaajaa. Jos pelaaja epäonnistuu, ohjelma esittää valittelunsa. Ohjelman suoritus päättyy, kun arvatuksi sanaksi syötetään "loppu". Tällöin ohjelma tulostaa oikeiden ja väärien arvausten määrän. Tehokkuussyistä sanojen hakeminen taulukosta on ohjelmoitava binäärihakua käyttäen.

Jatkokurssilaisena pääset helpommalla! Totetuta siis peli vaaditulla tavalla, mutta käytä hyväksesi Collections-kirjaston välineitä. Sanat esitetään MyString-oliona. Muista miten edellä mainittiin muutama esimerkki kirjaston välineistä: binarySearch, max, min, sort. Älä siis niitä tällä kertaa itse ohjelmoi.

Huom: Käytä siis tuota pyydettyä binarySearch-kirjastometodia, älä contains-aksessoria. Jälkimmäinen ei nimittäin toimi ilman lisätoimia! Selitystä alla.

Käyttöesimerkki (tietokone tässä kursiivilla):

Syötä onnensanat. Sana "loppu" lopettaa.
kissa
koira
amuletti
panda
loppu
Arvaa onnensana. Sana "loppu" lopettaa.
lusikka
Voi ei!
koira
Onnea!
amuletti
Onnea!
loppu
Oikeita: 2
Vääriä: 1


Miksi contains ei toimi:

Luennoja kävi pajassa ja panikoi, kun yksi opiskelija ei saanut contains-aksessoria toimimaan. Panikoi syyttä, koska ei tuota tehtävässä pyydettykään!

On silti ehkä opettavaista kuulla, miten metodin saisi toimimaan MyString-sisältöiselle kokoelmaoliolle. Tämä ei kuitenkaan kuulu koealueeseen:

Kokoelmaolioiden (esim. ArrayList) contains-metodi ei toimi oikein, ellei MyString-luokkaan lisää perityn equals-metodin syrjäyttävää metodia. Lisää siis MyString-luokkaan vaikkapa seuraava:

    public boolean equals(Object toinen) {
      return this.compareTo((MyString)toinen) == 0;
    }

Jo alkaa Lyyti kirjoittaa! :-)

Selitystä:


Joukot järjestykseen: JarjestyvaIntJoukko.java

Kokonaislukujoukkojen järjestys määräytyköön joukkojen mahtavuuden eli alkiolukumäärän perusteella. Muokkaa jostakin IntJoukko-luokan toteutuksestasi Comparable-järjestyvä luokka JarjestyvaIntJoukko ja havainnollista joukkojen järjestyvyyttä pienellä sovelluksella.

Oman Quicksortin ja Collections.sortin vertailua

Luennoilla ja kurssimateriaalin luvussa 6 (aliluku 7) vertailtiin pikajärjestämistä ja lisäysjärjestämistä ohjelmalla VertaileLisQuick.java (nanosekuntiversio: VertaileLisQuickB.java).

Olisi kiinnostavaa tietää, ovatko Javan Collections-luokan ohjelmoineet ammattilaiset osanneet tehdä paremman järjestämisalgoritmin kuin kurssilla tavattu ihan perusmuotoinen pikajärjestäminen.

Ja sehän selvitetään! Muokkaa mittausohjelmaa siten, että sillä vertaillaan pikajärjestämistä ja Collections.sort-metodia. Pikajärjestäminen järjestää tavallisen int[]-taulukon, kirjastometodi vastaavansisältöisen ArrayList-olion.

Tyypitettyä turvallista lukemista

Syötteiden lukemisen välineitä voidaan toteuttaa monenlaisella logiikalla. Pojimmiltaan kaikki ohjelmalle tarjottu syöte on pelkkää bittivirtaa. Yleensä ohjelmoijalle on onneksi tarjolla välineitä, joilla lukuoperaatioita voidaan kirjoittaa korkeammalla abstraktiotasolla: luetaan tavuja, merkkejä, rivejä, lukuja, ...

Lukemisen välineitä voidaan kutsua tyypitetyiksi, jos ohjelmoija voi/saa ilmaista, minkä tyyppisen tiedon hän haluaa lukea. Esimerkki tällaisesta on vaikkapa Javan Scanner-luokan tarjoama välineistö: nextInt, nextDouble, ...

Tyypitetyssä lukemisessa virheisiin voidaan suhtautua kahdella tavalla: Yksi tapa on antaa tyypiltään virheellisen syötteen keskeyttää ohjelman toiminta. Esimerkiksi kokonaisluvuksi kelpaamaton syöte aiheuttaa virhetilanteen, poikkeuksen, joka päättää ohjelman suorituksen ellei ohjelmoija itse sieppaa poikkeusta ja käsittele tapausta. Juuri tähän tapaan toimii Scanner-olio.

Toinen mahdollisuus on tehdä lukuoperaatioista sellaisia, että "virheitä ei voi tulla" – ainakaan tavanomaisissa virhetilanteissa. Esimerkiksi lukujen lukeminen voidaan tehdä sitkeäksi: lukuoperaatio itse väsymättä pyytelee kunnollista syötettä kunnes/jos viimein saa sellaisen. Tässäkin strategiassa jotkin vakavat virheet saattavat johtaa ohjelman suorituksen keskeyttämiseen, mutta sekin voidaan ehkä tehdä ymmärrettävämmin kuin Java-ohjelmissa oletusarvoisesti tapahtuu.

Rakennellaan vaiheittain kirjastoluokka Shell standardisyöttövirran turvalliseen tyypitettyyn lukemiseen.

Shell.java, vaihe 1: rivien lukeminen

Toteuta luokka Shell, jolla voi lukea stardardisyöttövirran rivejä yksi kerrallaan.

APIn lähtökohta:

Shell on syytä toteuttaa Scanner-olioa käyttäen. Riviksi kelpaa millainen merkkijono tahansa; Scannerin tapa lukea rivejä soveltuu siis sellaisenaan. Scannerin InputStream-parametrinen konstruktorikaan ei aiheuta poikkeuksia. Mutta metodi nextLine voi epäonnistua kahdella tavalla:

NoSuchElementException - if no line was found 
IllegalStateException  - if this scanner is closed

Vuorovaikutteisessa ohjelmassa ensinmainittu poikkeus aiheutuu, kun käyttäjä kirjoittaa ohjelmalle tiedostonlopun (Linux ctrl-d, Windows ctrl-z). Ensinmainittu poikkeus ei tällä kertaa ole virhetilanne, ks. metodin selitys. Varaudu kuitenkin toiseen poikkeukseen antamalla selkeä virheilmoitus ja lopettamalla ohjelman suoritus.

String rivi = Shell.kysyString("Anna rivi: ");

// Sama toisin:

System.out.print("Anna rivi: ");
rivi = Shell.kysyString("");

// vaiteliaana:
rivi = Shell.kysyString("");

//...

while (true) {  // "1.5-kertainen" toisto
  rivi = Shell.kysyString("");
  if (rivi == null)    // <<<<< toiston lopetus!
     break;
  System.out.println(rivi); 
}
...

Esimerkin suoritus (käyttäjä tässä kursiivilla):

Anna rivi: kissantassu
Anna rivi: pallo
mehukatti
piimä
piimä
aurinkokuningas
aurinkokuningas
[Linuxin ctrl-d tai Windowsin ctrl-z]

Shell.java, vaihe 2: kokonaislukujen lukeminen

Täydennetään Shell-luokkaa kokonaislukujen lukemisen taidolla.

API-täydennys:

Ohjelmaesimerkki:

int luku = Shell.kysyInt("Anna kokonaisluku! ");
System.out.println("Lukusi oli " + luku);

int luku = Shell.kysyInt("");
System.out.println("Lukusi oli " + luku);

luku =  Shell.kysyInt("Paljonko on tusina? ");
if (luku != 12) 
  System.out.println("Väärin!");

Käyttöesimerkki:

Anna kokonaisluku! Mörkö
Ei ole kokonaisluku!
Anna kokonaisluku! 4008
Paljonko on tusina? Mörkö
Ei ole kokonaisluku!
Paljonko on tusina? 13
Väärin!

Huomaa että kokonaisluvun lukeminen kannattaa toteuttaa siten, että koko syöttörivi käsitellään. Muuten saattaa tulla tuttuja(?) synkronointiongelmia. Lue siis tarjokas Stringinä. Salli myös ylimääräiset alku- ja loppuvälilyönnit.

Neuvo: Käytä luentomateriaalin luvussa 11 esiteltyjä välineitä Integer.parseInt(...) ja trim().

Shell.java, vaihe 3: rajoitettujen kokonaislukujen lukeminen

Täydennetään luokkaa Shell rajoitetulla välillä olevien kokonaislukujen lukemisen taidolla. Kuormitetaan yhä lisää metodia kysyInt.

API-täydennys:

Ohjelmaesimerkki:

int luku = Shell.kysyInt(1, "Anna positiivinen kokonaisluku! ");
System.out.println(luku);
luku = Shell.kysyInt("Anna ei-positiivinen kokonaisluku! ", 0);
System.out.println(luku);
luku = Shell.kysyInt(-100, "Anna luku väliltä -100-100! ", 100);
luku = Shell.kysyInt(12, "böö! ", 6);

Esimerkki ohjelmanpätkän suorituksesta:

Anna positiivinen kokonaisluku! Mörkö
Ei ole kokonaisluku!
Anna positiivinen kokonaisluku! -123
Anna positiivinen kokonaisluku! 123
123
Anna ei-positiivinen kokonaisluku! 1
Anna ei-positiivinen kokonaisluku! 0
0
Anna luku väliltä -100-100! 123
Anna luku väliltä -100-100! -99
-99
Exception in thread "main" java.lang.IllegalArgumentException: Tyhjä lukualue!
	at TiesMika.main(TiesMika.java:15)

Shell.java, vaihe 4: desimaalilukujen lukeminen

Täydennetään Shell-luokkaa vielä desimaalilukujen lukemisen taidolla.

API-täydennys:

Ohjelmaesimerkki:

double luku = Shell.kysyDouble("Anna luku! ");
luku = Shell.kysyDouble("Mikä on lieriön korkeus? ");
luku = Shell.kysyDouble("Miten kaukana on pitkällä? ");

Käyttöesimerkki:

Anna luku! Mörkö
Ei ole desimaaliluku!
Anna luku! -123.4
Mikä on lieriön korkeus? Mörkö
Ei ole desimaaliluku!
Mikä on lieriön korkeus? 3.14
Miten kaukana on pitkällä? 123.456e+10

Neuvo: Käytä luentomateriaalin luvussa 11 esiteltyjä välineitä Double.parseDouble(...) ja trim().

Suodattimia kirjallisuuden analysointiin

Tämä tehtäväsarja on luontevinta tehdä käyttöjärjestemän komentotulkki-ikkunassa. Jos välttämättä haluat käyttää NetBeans-ympristöä tässäkin sarjassa, ohjeet löytyvät tästä: NetBeans-projektien ajaminen komentotulkista.

Laaditaan joukko ohjelmia, jotka suodattavat (filtteröivät) tai muokkaavat standardisyöttövirtaa tavalla tai toisella erilaiseksi standarditulosvirraksi. Näitä ohjelmia voi toki suorittaa näppäimistöltä ja näytöltä, mutta niiden pääasiallinen käyttötarkoitus liittyy tiedostojen käsittelyyn uudelleenohjauksen avulla.

LaskeMerkit.java

Ohjelma laskee tiedoston merkkien määrän. Mukaan ei lasketa rivinvaihtomerkkejä. Ohjelman ainoa tuloste on yksi kokonaisluku.

Siis: Ohjelma lukee standardisyöttövirrasta rivejä ja kirjoittaa standarditulosvirtaan luettujen rivien lukumäärän.

Käyttöesimerkki (tässä merkki $ esittää komentotulkin kehoitemerkkiä eli "promptia"):

$ java LaskeMerkit
eka
toka
[Linuxin ctrl-d tai Windowsin ctrl-z]
7
$ java LaskeMerkit < tiedosto.txt
49876
$

Suurenna.java

Ohjelma muokkaa tiedostosta sellaisen, jossa kaikki pienet kirjaimet on muutettu suuriksi. Menettelyllä voi olla käyttöä, kun etsitään tekstimassasta sanoja piittaamatta siitä, onko ne kirjoitettu isoin vai pienin kirjaimin.

Siis: Ohjelma lukee standardisyöttövirrasta rivejä ja kirjoittaa standarditulosvirtaan lukemansa rivit sellaisina, joissa kaikki pienet kirjaimet on korvattu isoilla kirjaimilla.

Luokassa String on kelpo väline homman vaivattomaan hoiteluun.

Käyttöesimerkki:

$ java Suurenna
Ei tätä nyt ihan tähän ole tarkoitettu.
EI TÄTÄ NYT IHAN TÄHÄN OLE TARKOITETTU.
[Linuxin ctrl-d tai Windowsin ctrl-z]
$ java Suurenna < koe.txt
TÄMÄ KAIKKI TULEE TUOSTA
TIEDOSTOSTA!
$ java Suurenna < koe.txt > isokoe.txt
$

SuodataRiveja.java

Ohjelma suodattaa pois kaikki rivit, joilla ei esiinny komentoriviparametrina annettua merkkijonoa. Jos ohjelma käynnistetään ilman komentoriviparametria, se antaa esimerkin mukaisen virheilmoituksen.

Siis: Ohjelma lukee standardisyöttövirrasta rivejä ja kirjoittaa standarditulosvirtaan ne ja vain ne rivit, joilla esiintyy annettu merkkijono.

Käyttöesimerkki:

$ java SuodataRiveja
Virhe: komennolle pitää antaa suodatinsana!
$ java SuodataRiveja kissa
kissa kävelee.
kissa kävelee.
Kissa kävelee.
Koira kävelee.
Kaikissa tapauksissa.
Kaikissa tapauksissa.
moi vaan
[Linuxin ctrl-d tai Windowsin ctrl-z]
$ java SuodataRiveja kissa < teos.txt > kissateos.txt
$

RiveilleNumerot.java

Ohjelma muokkaa syötteestä sellaisen version, jossa joka rivin alkuun lisätään rivinumero, kaksoispiste ja välilyönti.

Siis: Ohjelma lukee standardisyöttövirrasta rivejä ja kirjoittaa standarditulosvirtaan kaikki luetut rivit siten, että kunkin rivin alkuun lisätään rivinumero, kaksoispiste ja välilyönti.

Käyttöesimerkki:

$ java RiveilleNumerot
Päivää!
1: Päivää!
abc
2: abc
[Linuxin ctrl-d tai Windowsin ctrl-z]
$ java RiveilleNumerot < koe.txt
1: eka rivi
2: ....
3: kolkku
4: eikä muuta
$ java RiveilleNumerot < tiedosto.txt > n_tiedosto.txt
$

VokKonsMuu.java

Ohjelma laskee tiedoston eräiden merkkiluokkien prosentuaalista osuutta tiedoston kokonaismerkkimäärästä komentoriviparametrin ohjaamalla tavalla:

Välilyöntejä, sarkaimia, rivin loppuja, ym. "white spacea" ei tässä laskennassa oteta mukaan kokonaismerkkimäärään.

Ohjelman ainoa tuloste on kokonaislukuna ilmaistu prosenttiluku.

Jos ohjelma käynnistetään ilman komentoriviparametria tai parametri on virheellinen, ohjelma antaa esimerkin mukaisen virheilmoituksen.

Käyttöesimerkki:

$ java VokKonsMuu
Komentoriviparametri (v, k tai e) puuttuu! 
$ java VokKonsMuu ö
Komentoriviparametri (v, k tai e) puuttuu!
$ java VokKonsMuu v
abc
defg
hij
[Linuxin ctrl-d tai Windowsin ctrl-z]
30
$ java VokKonsMuu v < MinaOlliJaOrvokki.txt
67
$

Kirjallisuudentutkimusta

Tehdään konkreettista kirjallisuudentutkimusta: Tutkitaan Juhani Ahon romaania Rautatie (Rautatie.txt) ja Mark Twainin teosta Huckleberry Finn (HuckleberryFinn.txt).

Käytä edellä tehtyjä välineitä ja luennolla esiteltyä ohjelmaa LaskeRivit.java. Laadi niistä komentoputkia seuraavia tutkimuksia varten.

Huomaa että teostiedostot ovat suoraan Gutenberg-projektin tarjoamassa muodossa. Jos haluat luotettavia tutkimustuloksia, tiedostoista kannattaa editoida pois turhat osat.

Kirjallisuudentutkimuksen työkalu

Edellä tehtiin yksittäisiä pieniä ohjelmia kirjallisuudentutkimuksen tarpeisiin. Nyt toteutetaan työkalu, jolla tutkija voi ehkäpä vaivattomammin tutkia tekstejä.

Voit pyrkiä uudelleenkäyttämään toteuttamiesi työkalujen algoritmeja tässä tehtävässä sillä kauhistellulla copy-and-paste-tekniikalla. Mutta odotahan vaan, mitä seuraa...

Tekstilaboratorio, vaihe 1: komentotulkki

Toteuta komentotulkkilogiikalla tekstilaboratorio, joka tarjoaa seuraavat palvelut:

Nykytiedosto on siis laboratorion keskeinen käsite. Tämä tiedosto on tutkimuksen ja käsittelyn kohteena.

Käyttöesimerkki:

$ java Tekstilaboratorio
Ei tiedostoa.
1 lue
2 tallenna
3 lopeta
1
Mikä? KesayonAjatelma.txt
Ei löydy tiedostoa KesayonAjatelma.txt
Ei tiedostoa.
1 lue
2 tallenna
3 lopeta
1
Mikä? KesayonUnelma.txt
Tiedosto: KesayonUnelma.txt
1 lue
2 tallenna
3 lopeta
4
Virheellinen komento!
Tiedosto: KesayonUnelma.txt
1 lue
2 tallenna
3 lopeta
3
Tiedostoa ei ole tallennettu. Lopetetaanko silti? (k=kyllä) EI!
Tiedosto: KesayonUnelma.txt
1 lue
2 tallenna
3 lopeta
2
Millä nimellä talteen? SyysyonUnelma.txt
SyysyonUnelma.txt on jo olemassa. Korvataanko se? (k=kyllä) k
Tiedosto: SyysyonUnelma.txt
1 lue
2 tallenna
3 lopeta
3
$

Operaatio merkkejä

Lisää laboratorioon komento merkkejä, joka laskee ja tulostaa nykytiedoston merkkien määrän. Mukaan ei lasketa rivinvaihtomerkkejä.

Käyttöesimerkki:

$ java Tekstilaboratorio
Ei tiedostoa.
1 lue       4 merkkejä
2 tallenna
3 lopeta
1
Mikä? Rautatie.txt
Tiedosto: Rautatie.txt
1 lue       4 merkkejä
2 tallenna
3 lopeta
4
189233
Tiedosto: Rautatie.txt     
1 lue       4 merkkejä
2 tallenna
3 lopeta
3
$

Operaatio suurenna

Lisää laboratorioon komento Operaatio suurenna, joka muokkaa nykytiedostosta sellaisen, jossa kaikki pienet kirjaimet on muutettu suuriksi.

Huomaa että nykytiedostoa muokattaessa ohjelman on syytä luoda tilapäinen aputiedosto, joka on syytä poistaa ohjelman loppuessa. Alkuperäisiä tiedostoja ei tietenkään saa muuttaa.

Käyttöesimerkki:

$ java Tekstilaboratorio
Ei tiedostoa.
1 lue       4 merkkejä
2 tallenna  5 suurenna
3 lopeta
1
Mikä? Rautatie.txt
Tiedosto: Rautatie.txt
1 lue       4 merkkejä
2 tallenna  5 suurenna
3 lopeta
5
Tiedosto: Rautatie.txt
1 lue       4 merkkejä
2 tallenna  5 suurenna
3 lopeta
3
$

Operaatio suodata

Lisää laboratorioon komento suodata, joka poistaa nykytiedostosta kaikki rivit, jotka eivät sisällä annettua merkkijonoa.

Huomaa että nykytiedostoa muokattaessa ohjelman on syytä luoda tilapäinen aputiedosto, joka tietenkin ohjelman suorituksen päättyessä on poistettava.

Käyttöesimerkki:

$ java Tekstilaboratorio
Ei tiedostoa.
1 lue       4 merkkejä
2 tallenna  5 suurenna
3 lopeta    6 suodata
1
Mikä? Rautatie.txt
Tiedosto: Rautatie.txt
1 lue       4 merkkejä
2 tallenna  5 suurenna
3 lopeta    6 suodata
6
Anna suodatinsana! kissa
Tiedosto: Rautatie.txt
1 lue       4 merkkejä
2 tallenna  5 suurenna
3 lopeta    6 suodata
3
$

Operaatio numeroi

Lisää laboratorioon komento numeroi, joka muokkaa nykytiedostoa siten, että joka rivin alkuun lisätään rivinumero, kaksoispiste ja välilyönti.

Huomaa että nykytiedostoa muokattaessa ohjelman on syytä luoda tilapäinen aputiedosto, joka tietenkin ohjelman suorituksen päättyessä on poistettava.

Käyttöesimerkki:

$ java Tekstilaboratorio
Ei tiedostoa.
1 lue       4 merkkejä   7 numeroi
2 tallenna  5 suurenna
3 lopeta    6 suodata
1
Mikä? Rautatie.txt
Tiedosto: Rautatie.txt
1 lue       4 merkkejä   7 numeroi
2 tallenna  5 suurenna
3 lopeta    6 suodata
7
Tiedosto: Rautatie.txt
1 lue       4 merkkejä   7 numeroi
2 tallenna  5 suurenna
3 lopeta    6 suodata
3
$

Operaatio vokaali

Lisää laboratorioon komento vokaali, joka tutkii ja ilmoittaa nykytiedoston vokaalien, konsonanttien ja erikoismerkkien prosentuaaliset osuudet. Välilyöntejä, sarkaimia, rivin loppuja, ym. "white spacea" ei tässä laskennassa oteta mukaan kokonaismerkkimäärään.

Käyttöesimerkki:

$ java Tekstilaboratorio
Ei tiedostoa.
1 lue       4 merkkejä   7 numeroi
2 tallenna  5 suurenna   8 vokaali
3 lopeta    6 suodata
1
Mikä? Teos.txt
Tiedosto: Teos.txt
1 lue       4 merkkejä   7 numeroi
2 tallenna  5 suurenna   8 vokaali
3 lopeta    6 suodata
8
54 % vokaaleja
29 % konsonantteja
7 % erikoismerkkejä
Tiedosto: Teos.txt
1 lue       4 merkkejä   7 numeroi
2 tallenna  5 suurenna   8 vokaali
3 lopeta    6 suodata
3
$

Eikö enää koskaan copy-pastea!

"Refaktorointia"

Kahdessa erilaisesessa kirjallisuudentutkimuskaluston toteutustavassa on paljon yhteistä. Ja copy-paste-ohjelmointi ei ole kovin tyylikästä.

Kehittele jokin tapa, ohjelman rakenne, ohjelman arkkitehtuuri, jolla voit toteuttaa molemmat palvelut jouttumatta uudelleenkirjoittamaan tai kopioimaan samaa ohjelmakoodia sinne tänne.

Hahmottele arkkitehtuurisi myös Javan luokkakaluston avulla ihan konkreettisina luokkina ja muina rakenteina. Varsinaiset algoritmien toteutukset voit tähän luurankoon jättää tekemättä

Tai jos teet ensin tämän tehtävän, 2. ja 4. tehtäväsarjan tekeminen voi olla helppoa... ;-)