Java-ohjelmointi koe 17.12.2003 Tehtävä 4 Arvostelu Jaakko Nenonen ---------------------------------------------------------------------------- Tehtävänanto: ---------------------------------------------------------------------------- Käytössäsi on tekstitiedostojen lukemiseen luokka Syottotiedosto. Luokalla on konstruktori Syottotiedosto(String nimi). Parametrina annetaan tiedoston nimi. Rivejä luetaan aksessorilla public String lueRivi() Tiedoston loppuminen ilmaistaan null-arvolla. Käytössäsi on myös tekstitiedostojen kirjoittamisen väline, luokka Tulostiedosto. Luokalla on konstruktori Tulostiedosto(String nimi). Parametrina annetaan tiedoston nimi. Rivejä kirjoitetaan aksessorilla public void kirjoitaRivi(String rivi) Sekä molempien tiedostojen luonti että luku- ja kirjoitusoperaatiot voivat aiheuttaa poikkeuksen Exception. Laadi keskusteleva ohjelma KopioiJoulurivit, joka kysyy ensin kaksi tiedoston nimeä, syöttötiedoston ja tulostustiedoston. Sitten ohjelma tekee syöttötiedostosta kopion, jonne kopioidaan vain kaikki ne syöttötiedoston rivit, joilla esiintyi yksi tai useampi sanoista "joulu", "kuusi", "pukki" tai "puuro". Toinen kysytty nimi, tulostustiedosto, on siis luotavan kopiotiedoston nimi. Ohjelman KopioiJoulurivit pitää itse käsitellä järkevällä ja käyttäjäystävällisellä tavalla tiedostojen luomisen ja käytön mahdollisesti aiheuttamat Exception-poikkeukset. (12 pistettä) ---------------------------------------------------------------------------- Yleistä ---------------------------------------------------------------------------- Tehtävä osattiin kohtuullisen hyvin vaikka se oli vaikea ja tehtävänantokin oli ymmärretty pääasiassa oikein. Mielestäni arvostelin melko hellämielisesti eikä pienistä syntaksivirheistä menettänyt pisteitä. Täysiin pisteisiin vaadittiin kuitenkin moitteeton ratkaisu. Käytössä siis oli Syottotiedosto-luokka ja Tulostiedosto-luokka, eikä niitä pitänyt koodata itse. Jos koodasi, pisteitä sakotettiin roimasti. Lisäksi Lue-luokka oli käytössä vaikka tehtävänannossa sitä ei erikseen mainittu. Jos Lue-luokan toimintoja oli tehnyt uudelleen ja siitä myös sakotettiin. Poikkeuskäsittelynä vaadittiin useita try-catch lauseita, jotka sisälsivät virhetilanteen kuvauksen. Throws-määreen käyttö main-metodin otsikossa ei riittänyt, eikä myöskään se, että oli laittanut koko ohjelman rungon yhden try-catchin sisään. Tällaiset ratkaisut eivät ole käyttäjäystävällisiä koska käyttäjälle ei kerrota missä virhe tapahtui. Käyttäjäystävällisyyteen määritelmään laskettiin myös System.out.println:n käyttö. Mikäli tulosteita ei käyttänyt ollenkaan,sakotettiin muutama piste. Myös catchien sisällä tuli olla tulosteita. Jouluisen rivin tarkistukseen tuli käyttää indexOf-aksessoria tai vaihtoehtoisesti sai koodata oman metodin, joka teki saman oikein. Aika moni ei muistanut aksessorin nimeä. Väärästä nimestä ei kuitenkaan menettänyt pisteitä. Kuitenkin jos omanimetty aksessori toimi eritavalla kuin indexOf, tuli selostaa kuinka se toimi. Muuten menetti pisteitä. Seuraavassa on esimerkkiratkaisu. Kuten tavallista ratkaisutapoja oli useita ja tämä on vain esimerkki. ---------------------------------------------------------------------------- Esimerkkiratkaisu ---------------------------------------------------------------------------- public class KopioiJoulurivit { private final static String[] JOULUSANAT = { "joulu", "kuusi", "pukki", "puuro" }; public static void main(String args[]) { Syottotiedosto syotto = null; Tulostiedosto tulos = null; boolean ok = true; System.out.println("Jouluisten rivien kopiointiohjelma"); System.out.println("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"); do { System.out.println("Anna syöttötiedoston nimi, josta joulurivit kopioidaan"); String nimi = Lue.rivi(); ok = true; try { syotto = new Syottotiedosto(nimi); } catch (Exception e) { System.out.println("Tiedostoa " + nimi + " ei ole olemassa!"); ok = false; } } while (!ok); System.out.println("Anna tulostiedoston nimi, johon joulurivit kopioidaan"); String nimi = Lue.rivi(); try { tulos = new Tulostiedosto(nimi); } catch (Exception e) { System.out.println("Tiedoston " + nimi + " luonti ei onnistunut!"); System.out.println("Ohjelman suoritus keskeytetään."); System.exit(0); } String rivi; try { while ((rivi = syotto.lueRivi()) != null) { for (int i = 0; i < JOULUSANAT.length; i++) { if (rivi.indexOf(JOULUSANAT[i]) > -1) { tulos.kirjoitaRivi(rivi); break; // ilman breakia esim rivi "joulupukki" // tulostettaisiin kahdesti. Miksi? // breakin puuttumisesta tuli -0.5p } //VAIHTOEHTOINEN TAPA TARKISTAA JOULUISUUS (muitakin tapoja oli): // // if (rivi.indexOf("joulu")>-1 || // rivi.indexOf("pukki")>-1 || // rivi.indexOf("kuusi")>-1 || // rivi.indexOf("puuro")>-1) // tulos.kirjoitaRivi(rivi); } catch (Exception e) { System.out.println("Virhe tiedostojen käsittelyssä! "+e); System.out.println("Ohjelman suoritus keskeytetään."); System.exit(0); } System.out.println("Kopiointi on suoritettu onnistuneesti."); } } ---------------------------------------------------------------------------- Seuraavista asioista sai pisteet: ---------------------------------------------------------------------------- alustus: (3p) -syöttötiedoston luonti 0.5p, try-catch 1p -tulostiedoston luonti 0.5p, try-catch 1p luuppi: (7p) -luupin oikeellisuus 2p -syöttötiedoston luku 0.5p -jouluisen rivin tarkistus 2p -tulostiedostoon kirjoitus 0.5p -try-catch 2p muuta: (2p) -ratkaisun käyttäjäystävällisyys 2p - jos ei ollut s.o.p ollenkaan sai 0p/2p käyttäjäystävällisyydestä, - jos tulosteita oli muualla mutta ei catch-osioissa, sai 1p/2p käyttäjäystävällisyydestä yhteensä: 12p ---------------------------------------------------------------------------- Seuraavista sakotettiin pisteitä: ---------------------------------------------------------------------------- Yleiset -------- - pahat syntaksivirheet -0.5p tai -1p per virhetyyppi - toteutus olioratkaisuna (kömpelöä) -1p Poikkeuskäsittely ----------------- - jos ei ollut poikkeuskäsittelyä ollenkaan sai koko tehtävästä max 6 pistettä - main-metodissa throws Exception, max 8p - yksi try-catch koko ohjelman ympärillä, max 9p - catchin jälkeen ohjelman suoritus jatkuu eikä virhettä korjata -1 tai -2p (yleisimpiä virheitä) - tyhjä catch-osa -2p - try-catcheissä ei ole tulosteita käyttäjälle -1p - Ei ollenkaan tulosteita koko ohjelmassa -2p Alustusvaihe ------------- - oliomuuttujien määrittelyä -1p - luokasta on unohtunut main(..)-metodin määritys tai luokan otsikko -1p - syötteet komentoriviparametreina -1p (epäkäyttäjäystävällistä) - Turhaa tiedostokäsittelyä -2p tai -3p esim. File f = new File(..); f.exists()... BufferedReader(...) jne - Lue.rivi()-aksessoria ei käytetty vaan yritetään tehdä se itse BufferedReaderilla -2p - Lue.rivi()-metodin ympärillä try-catch vaikka se ei aiheuta poikkeusta -1p - Syöttö- ja tulostiedoston luonneissa ei ollenkaan try-catchejä -3p - Syöttö- ja tulostiedoston luonnissa käytetään vain yhtä try-catchiä -1p - selitys "tulostiedosto luodaan kuten syöttötiedosto edellä" -2p toistorakenne -------------- - käsittämätön toistorakenne (huonoa ohjelmointityyliä) -3p, esim: for(;;) {....} for (int i=1, j=1; (apu =st.lueRivi())!=null; i++) {...} while(true) {...} - lohkorakenteet eivät ole toistorakenteessa sisäkkäin, esim: try { do { } catch (..) {..} } while(..); -3p tämä outo virhe oli yllättävän monella! - rivien lukemisella ja kirjoittamisella ei ole try-catchiä -3p - syöttötiedoston aksessoria lueRivi kutsutaan useita kertoja yhdellä toistokierroksella -1p - syöttötiedoston lukeminen päättyy NullpointerExceptioniin (ehkä yleisin virhe) -1p NullpointerException tulee esim. seuraavassa toistorakenteessa: do { rivi = syotto.lueRivi(); if (rivi.indexOf("joulu"))..... // ^ nullpointerexception kun rivi == null while (rivi != null); virheen sai korjattua käyttämällä do-whilen sijaan whileä rivin jouluisuuden tarkistus ---------------------------- - etsitään vain esim sanaa "joulu" ja muut joulusanat jätetään huomioimatta -1p - jouluisen rivin tarkistamiseen käytetään jotain keksittyä Stringin aksessoria, eikä kerrota miten se toimii -1p Esim: rivi.includes("joulu") rivi.substring()=="joulu" rivi.equals("pukki") rivi.exists("kuusi") rivi.compareTo("pukki")>-1 rivi.matches("joulu") <--- olemassa oikeasti mutta järjetöntä käyttää tässä yhteydessä - if (rivi on jouluinen).... -2p ---------------------------------------------------------------------------- seuraavista moitittiin mutta ei menettänyt pisteitä ---------------------------------------------------------------------------- - import-määreen käyttö turhaan - pienet syntaksivirheet, puolipisteen tai sulkeen puuttuminen - lohkon (esim Try{...}) sisällä määritettyä muuttujaa käytetään lohkon ulkopuolella - muuttujalla ei ole alkuarvoa, ja arvo asetetaan vasta lohkon sisällä jolloin ei mene kääntäjästä läpi. ---------------------------------------------------------------------------- Arvosanajakauma ---------------------------------------------------------------------------- 0 **************** (16kpl, joista 4 tyhjiä) 1 ********** (10) 2 *************** (15) 3 ****** (6) 4 *********** (11) 5 ******* (7) 6 ********* (9) 7 ************ (12) 8 ************************ (24) 9 ************** (14) 10 **************** (16) 11 ******************* (19) 12 ************* (13) 172 vastausta joista 4 tyhjiä keskiarvo 6.5