Java-ohjelmointi, syksy 1999 Arvosteluperusteet, tehtävä 4. Olli Lahti Tehtävässä pyydettiin laatimaan ohjelma kahden tiedoston vertailemiseen. Apuna tuli käyttää luokkaa Syottotiedosto, joka tarjoaa (ainakin) tehtävänannossa kuvatut palvelut. Tuota luokkaa ei siis pitänyt eikä saanut toteuttaa itse, minkä muutamaa tapausta lukuun ottamatta kaikki olivat ymmärtäneetkin. Ratkaisuksi riitti esittää luokkaan EroavatRivit sijoitettu main-metodi, joka suorittaa kaiken tarvittavan. Toki vielä 'olioperustaisemmat' toteutukset hyväksyttiin myös. Arvostelu perustuu oheiseen esimerkkiratkaisuohjelmaan, jonka eri osista sai enimmillään 15 pistettä seuraavasti: * yleisrakenne (static!) sekä suorituksen päättäminen eri tilanteissa 2 p. * komentoriviparametrien tarkistus (args.length) 1 p. * tiedostojen avaus eli Syottotiedosto-olioiden luonti ( ... = new Syottotiedosto(args[0/1]); ) siten, että poikkeuksen sattuessa suoritus päättyy hallitusti ja käyttäjälle kerrotaan, mistä on kyse 4 p. --> ilman try-catch 2 p. vähemmän * tiedostoista lukeminen ( ... = olio.lueRivi(); ) siten, että poikkeuksen sattuessa suoritus päättyy hallitusti ja käyttäjälle kerrotaan, mistä on kyse 3 p. --> ilman try-catch 2 p. vähemmän * niin kauan kuin kummassakin tiedostossa riittää lukemista (saatu rivit !=null JA !=null), merkkijonovertailu sekä riviparin tulostus, mikäli rivit poikkeavat toisistaan 3 p. * jos täsmälleen toisessa tiedostossa riittää lopuksi luettavaa, kaikkien loppujen tiedoston rivien tulostus 2 p. Yleisimpiä pisteiden vähentämiseen johtaneita virheitä olivat: max. - 3 p. : algoritmissa vikaa, esim. hukkaa rivejä (toisto, if, break, ...) tai viittaa osoittimen null-arvolla (kuten r1.equals(r2) ja ei estetä viittauksen suorittamista, kun r1==null) - 2 p. : muuttujien lohkonäkyvyyttä ei ole ymmärretty --> hyvin monet kirjoittivat try { tyyppi mja = ... } catch (...) { ... } ja käyttivät jäljempänä muuttujaa mja sijoitettuine arvoineen - 2 p. : merkkijonovertailua ei ole toteutettu muodossa eka.metodi(toka) vaan esim. vertailtu suoraan olioviitteitä --> vertailumetodin nimen muistamisesta väärin ei ole sakotettu -- paitsi jos String-luokan oikean metodin paluuarvon käsittelyssä sekoitetaan keskenään tyypit int ja boolean --> tapauksesta riippuen (eka==toka) on voitu tulkita vakioksi (false) muun algoritmin kannalta - 2 p. : ylimääräisiä tulostuksia ja/tai tulostaa jotakin myös silloin, kun tiedostojen sisällöt ovat täsmälleen samat Seuraavasta kahdesta yleisestä virheestä ei sen sijaan juurikaan sakotettu: - ehdottoman loogisen operaation (joko & tai |) sekoittaminen ehdolliseen loogiseen operaatioon (joko && tai ||) - muuttujan määrittely while-lauseen ehdossa for-lauseen tapaan (sallittua siis vain for-lauseessa) Toistuva null-arvojen lukeminen lueRivi()-metodilla oli toki sallittua kuten myös yhden ja saman try-catch-lauseen ulottaminen koskemaan kaikkia Syottotiedosto-olioiden metodikutsuja. ***** Handling exceptions was not required in the English version of this question. For the Finnish programs this was worth max. four points. Thus the remaining 11 points were approximately scaled up to 15 points, which means about one point more to be had from each (other) part. ---------------------------------------------------------------------- /* Java-ohjelmointi, koe 16.12.1999 * tehtävän 4 esimerkkiratkaisu - Olli Lahti - * EroavatRivit -- ohjelma kahden tiedoston vertailemiseen */ public class EroavatRivit { public static void main(String[] args) { if (args.length != 2) { System.out.println("Anna komentorivillä kaksi tiedostoa!"); return; // Poistu pääohjelmasta, // myös System.exit(0); kävisi. } Syottotiedosto ekasyotto, tokasyotto; try { ekasyotto = new Syottotiedosto(args[0]); tokasyotto = new Syottotiedosto(args[1]); } catch (Exception e) { System.out.println("Virhe tiedostojen avaamisessa."); System.out.println("Ohjelman suoritus päättyy."); return; } String ekarivi, tokarivi; do { try { ekarivi = ekasyotto.lueRivi(); tokarivi = tokasyotto.lueRivi(); } catch (Exception e) { System.out.println("Virhe tiedoston lukemisessa!"); System.out.println("Ohjelman suoritus päättyy."); return; } if (ekarivi != null && tokarivi != null) { // Kummassakin tiedostossa riitti luettavaa if ( ! ekarivi.equals(tokarivi) ) { // eroavat rivit System.out.println("1>" + ekarivi +"<"); System.out.println("2>" + tokarivi +"<"); } } else { // Ainakin toinen tiedosto on loppunut, // tulosta toisen mahd. jäljelle jääneet rivit if (ekarivi != null) System.out.println("1>" + ekarivi +"<"); if (tokarivi != null) System.out.println("2>" + tokarivi +"<"); } } while ((ekarivi != null || tokarivi != null)); // Toista niin kauan kuin jommassa kummassa tiedostossa // riittää luettavaa (eikä tapahdu poikkeusta). } } ---------------------------------------------------------------------- ------- This class was assumed to be written by someone else: -------- ---------------------------------------------------------------------- // LIITE tehtävän 4 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.) }