Java-ohjelmointi, syksy 2000 Arvosteluperusteet, tehtävä 3. Olli Lahti ======================================================== ESIMERKKIVASTAUS, jonka mukainen toiminta tuli toteuttaa ======================================================== public class Sanatiedosto { // * myös aliluokaksi oikein tehty ratkaisu kelpaa private Syottotiedosto syotto; // viite olioon, joka tarjoaa tiedostopalvelun private String nykyrivi; // käsiteltävänä oleva tiedostosta luettu rivi private int jatkopaikka; // positio, jota aiempi rivin osa on jo käsitelty // sanojen loputtua tai virhetilanteessa syotto on null (muilla ei merkitystä) // * myös muu lopputilan koodausstrategia kelpaa // * myös pientä määrää apumuuttujia tai aputaulukkoa saa käyttää public Sanatiedosto(String nimi) { // konstruktori // havaitse ja käsittele nimen puuttuminen if (nimi == null) { this.syotto = null; return; // poistu konstruktorista; luotiin "tyhjän tiedoston" olio } // yritä alustaa tiedoston luku (eli avata tiedosto) try { this.syotto = new Syottotiedosto(nimi); // komponenttiolion luonti } catch (Exception e) { this.syotto = null; return; // poistu konstruktorista; luotiin "tyhjän tiedoston" olio } // tiedosto on käytettävissä; lue käsiteltäväksi ensimmäinen rivi this.noudaRivi(); } public String lueSana() { // (seuraavassa vain yksi tapa toteuttaa metodi) while (this.syotto != null) { // ellei tiedosto ehdy, sanan on löydyttävä int sananpaikka = -1; // tavatun sanan alkukohta (alkuarvolla ei väliä) boolean sanaAlkanut = false; while (this.jatkopaikka < nykyrivi.length()) { // ----------------------- if (this.nykyrivi.charAt(this.jatkopaikka) == ' ') { if (sanaAlkanut) // kesken ollut sana päättyi; palauta sana ennen väliä return nykyrivi.substring(sananpaikka, this.jatkopaikka); else this.jatkopaikka++; // kelaa eteenpäin } else { // kyseessä merkityksellinen merkki if (!sanaAlkanut) { sananpaikka = this.jatkopaikka; sanaAlkanut = true; } this.jatkopaikka++; // kelaa eteenpäin } } // -------------------------------------------------------------------- if (sanaAlkanut) // kesken ollut sana päättyi rivin loppumiseen; palauta koko loppuosa return nykyrivi.substring(sananpaikka); // Rivi loppui ennen kuin sana alkoikaan - jatka seuraavan rivin alusta! this.noudaRivi(); } return null; // tiedosto ehtyi (tai tapahtui virhe) } private void noudaRivi() { // yksityinen apumetodi uuden rivin lukemiseen this.jatkopaikka = 0; // jatketaan sanailua vähintään uuden rivin alusta try { this.nykyrivi = syotto.lueRivi(); // jättää itse rivinvaihtomerkin pois if (this.nykyrivi == null) this.syotto = null; // lueRivi()-metodi kertoi tiedoston loppuneen } catch (Exception e) { this.syotto = null; } } // * OK valita seuraavan sanan haku joko vanhan kutsun lopuksi tai uuden aluksi // (hyväksyttiin myös kaikkien tdoston sanojen lukeminen varastoon taulukkoon) // PÄÄOHJELMA (samassa tai eri luokassa - olio on aina luotava!) public static void main(String [] args) { System.out.println("Anna tiedoston nimi, jonka sanat lasketaan: "); String nimi = Lue.rivi(); if (nimi == null) return; // ei jatketa, jos tuli null Sanatiedosto laskettava = new Sanatiedosto(nimi); // olion luonti int lkm = 0; while (laskettava.lueSana() != null) lkm++; System.out.println("Tiedostossa oli sanoja " + lkm + " kpl."); } } ========== PISTEYTYS: ========== - luokan Sanatiedosto yleisrakenne (myös extends Syottotiedosto hyväksyttiin) 1 p. - kentät kesken olevan rivin kirjanpitoon 1 p. - järkevä koodaus olion joutumiselle 'epäkuntoon' 1 p. - private Syottotiedosto -kenttä sekä konstruktori try-catchein (tai oikea periyttämistoteutus jos valittu tapa extends Syottotiedosto) 3 p. - rivikirjanpitokenttien alustaminen 1 p. - lueSana()-metodissa seuraavan sanan täsmällinen selvittäminen kaikki mahdollinen alku- ja loppu- tyhjä sekä tyhjät rivit huomioon ottaen; kirjan- pidon ylläpito; sanan palauttaminen (tai null) 3 p. - lueSana()-metodin muut osat 1 p. - rivin lukeminen Syottotiedosto-olion avulla (tai yliluokan metodin jos valittu tapa extends Syottotiedosto) try-catchein sekä vanhan rivin korvaaminen Sanatiedosto-olion kirjanpidossa 3 p. Kokeiluohjelma (main-metodi): - yleisrakenne sekä algoritmin idea oikein 1 p. - olion luonti käytettäväksi (ellei luettu null) 1 p. - olion käyttö (mukaanluettuna paluuarvon null tarkkaileminen sekä sanojen tarkka laskeminen) 1 p. ----- yhteensä 17 p. =============================================================== // LIITE tehtävän 3 esimerkkiratkaisuun: // tiivis toteutus luokalle Syottotiedosto. // * Tätä ei siis pitänyt eikä saanut ohjelmoida kokeessa, * // * mutta tiedoksi siltä varalta, että joku haluaa testata. * import java.io.*; public class Syottotiedosto { BufferedReader lukupuskuri; Syottotiedosto(String nimi) throws Exception { lukupuskuri = new BufferedReader( new InputStreamReader( new FileInputStream(nimi) ) ); // Konstruktorin kutsujan tulee käsitellä mahdollinen poikkeus. } public String lueRivi() throws Exception { return lukupuskuri.readLine(); // Metodin kutsujan tulee käsitellä mahdollinen poikkeus. } // Syottotiedosto-luokan avulla avattua tiedostoa ei tarvitse // erikseen sulkea. (Luokka FileInputStream sisältää finalize- // metodin, jonka ansiosta tiedosto aina viimein suljetaan.)