Arvutiteaduse instituut
  1. Kursused
  2. 2024/25 sügis
  3. Objektorienteeritud programmeerimine (IT mitteinformaatikutele) (LTAT.SO.003)
EN
Logi sisse

Objektorienteeritud programmeerimine (IT mitteinformaatikutele) 2024/25 sügis

  • Kodutööd ja praktikumid
  • Loengud
  • Kursuse korraldus
  • IDE juhendid
  • Silumisest

Praktikum 9

Vood. Failid.

Teemad

Pakett java.io. Klass File. Sisend- ja väljundvood. Baidivood ja märgivood. Puhverdatud vood. Failide kokkupakkimine.

Pärast selle praktikumi läbimist oskab üliõpilane

  • realiseerida Java vahenditega põhilisemaid failioperatsioone;
  • luua vooge;
  • voost lugeda ja sinna kirjutada.

Selles praktikumis õpitavad Java vood kasutavad hoolega erindite (exception) võimalusi. Kuidas veahaldus ja erindid toimivad, õpime täpsemalt järgmises praktikumis. Selles praktikumis proovime erindite kasutamist vältida. Selle jaoks lisage iga oma meetodi päisesse deklaratsioon throws Exception, et kompilaator erindite mittekasutamise pärast virisema ei hakkaks. (Kui IDE soovitab selle asemel try-catch plokke kasutada, siis ärge teda kuulake.)

Failisüsteemi toimingud

Failidega töötamiseks vajalikud klassid on koondatud paketti java.io. Selle klassid võimaldavad muuta nii failide sisu kui ka nimetada ümber, kustutada ja luua faile ning katalooge. Failide ja kataloogidega tegelemiseks vajalikud meetodid asuvad klassis File. Natuke eksitavalt tähistavad File objektid nii faile kui ka katalooge. File objekti saab luua kirjutades new File("failinimi"). Pane tähele, et see ei loo kettale uut faili vaid kõigest objekti, mis olemasolevat (või mitteolevat) faili tähistab.

//Programm, mis jooksvas kataloogis leiab laiendita failid ja lisab neile laiendi
import java.io.File;

public class MuudaFailinimed {
  public static void main(String[] args) throws Exception {
    File dir = new File(".");
    String[] failid = dir.list();
    for (String fail : failid) {
      File vana = new File(fail);
      if (vana.isFile() && !fail.contains(".")) {
        File uus = new File(fail + ".txt");
        vana.renameTo(uus);
        System.out.println("Muudetud " + vana.getName() + " -> " + uus.getName());
      }
    }
  }
}

Ärge unustage, et Windows kasutab eraldajana tagurpidi kaldkriipsu \, aga Mac ja Linux kasutavad edasipidi kaldkriipsu /. Kirjutage kood nii, et see toimiks kõigi operatsioonisüsteemidega. Ärge kirjutage koodis "somefolder/myfile" vaid "somefolder" + File.separatorChar + "myfile" või new File("somefolder", "myfile").

Sissejuhatus voogudesse

Peaaegu kõik andmete lugemise ja kirjutamise operatsioonid Javas toimivad läbi voogude. Vood jaotuvad suures plaanis kaheks: sisendvood ja väljundvood. Leiduvad vastavad abstraktsed klassid InputStream ja OutputStream, mis oskavad baite sisse lugeda ja välja kirjutada. Siin on meie jaoks tähtsamad meetodid:

abstract class InputStream {
  // tagastab järgmise baidi väärtuse või -1, kui rohkem lugeda ei saa
  int read();
  // loeb voost 1 <= n <= buf.length baiti ja paneb need massiivi buf
  // tagastab loetud baitide arvu või -1, kui rohkem lugeda ei saa
  // NB! loeb täiesti ettearvamatu arvu baite! ära eelda, et buf alati täidetud saab.
  int read(byte[] buf);
  // vabastab vooga seotud ressurssid
  void close();
}

abstract class OutputStream {
  // kirjutab ühe baidi
  void write(int b);
  // kirjutab osa massiivist
  void write(byte[] buf, int offset, int length);
  // vabastab vooga seotud ressurssid
  void close();
}

Klassid InputStream ja OutputStream on abstraktsioonid, mis keskenduvad ainult kõige tähtsamale - baitide lugemine ja kirjutamine. Keerulisemate andmetega (täisarvud, ujukomaarvud, tekst) töötamiseks on olemas erinevad abiklassid, mis meie elu lihtsamaks teevad. Sellest paar lõiku hiljem.

Siin praktikumis õpime peamiselt failidest lugemist ja sinna kirjutamist. Failidesse voo avamiseks kasutage vastavaid voogude realisatsioone:

try (InputStream sisse = new FileInputStream("kassipilt.jpg")) {
  // faili kasutamine
}

try (OutputStream välja = new FileOutputStream("teine_kassipilt.jpg")) {
  // faili kasutamine
}

Vaatame nüüd näidet voogude kasutamisest. Meetod kopeeri loeb sisendvoost tükkhaaval baite sisse ja kirjutab pärast iga tüki lugemist selle kohe väljundvoogu. Kuna baitide ükshaaval lugemine ja kirjutamine on meeletult ebaefektiivne, tõstetakse korraga kuni 1024 baiti.

//Programm, mis teeb koopia etteantud failist:
import java.io.*;

public class KopeeriFail {

  private static void kopeeri(String algne, String koopia) throws Exception {
    // sulgudes semikoolonit kasutades saab mitu faili avada
    try (InputStream sisse = new FileInputStream(algne);
         OutputStream välja = new FileOutputStream(koopia)) {
      byte[] puhver = new byte[1024];
      int loetud = sisse.read(puhver);
      while (loetud > 0) {
        välja.write(puhver, 0, loetud); // ainult andmetega täidetud osa!
        loetud = sisse.read(puhver); // loeme järgmise tüki
      }
    }
  }

  public static void main(String[] args) throws Exception {
    if (args.length != 1) {
      System.out.println("Kas sa andsid käsurealt faili nime?");
      System.exit(1);
    }
    kopeeri(args[0], args[0] + ".copy");
  }
}

Tuletage meelde juhend käsurea parameetrite määramiseks Eclipse ja IntelliJ jaoks.

Ülaltoodud kood on üsna keeruline, aga teeb väga lihtsat asja - kopeerib faili. Miks ei saaks lihtsalt ühe käsuga kogu faili sisse lugeda ja teise käsuga see välja kirjutada? Probleem on selles, et failid saavad olla oluliselt suuremad, kui arvuti mälu. Kõike korraga sisse lugedes võib mälu lihtsalt otsa saada. Andmeid tükkhaaval töödeldes ei pea kunagi selle pärast muretsema.

Ülesanne 1 (kontroll)

Muuta eelmist programmi nii, et juhul, kui üritatakse kopeerida kataloogi, väljastatakse vastav teade ja programm lõpetab töö. Juhul, kui tegemist on failiga, väljastada ekraanile faili suurus ja aeg, millal faili viimati muudeti.

Voogude abiklass teksti kodeerimiseks

Vaadeldud klassid InputStream ja OutputStream tegelevad ainult baitidega. Tihti on meil aga vaja töötada tekstiliste andmetega ja oleks ebapraktiline käsitsi teksti baitideks teisendada ja vastupidi. Selle jaoks on olemas abiklassid InputStreamReader ja OutputStreamWriter, millega saab mugavalt tähemärke baidi-voogudest lugeda ja kirjutada. Tähtis on teada järgnevaid meetodeid:

class InputStreamReader {
  // loeb ja dekodeerib baite tähemärkideks
  // toimib nagu InputStream.read, aga puhvri tüüp on byte[] asemel char[]
  int read(char[] cbuf);
}

class OutputStreamWriter {
  // kodeerib sõne baitideks ja kirjutab need voogu
  void write(String str);
}

Vastavate abiklasside kasutamiseks tuleb ette anda baidi-voog, mida "abistatakse" ja määrata teksti kodeering:

try (InputStream sisse = new FileInputStream("sisend.txt");
     InputStreamReader tekstSisse = new InputStreamReader(sisse, "UTF-8")) {
  // faili kasutamine
}

try (OutputStream välja = new FileOutputStream("väljund.txt");
     OutputStreamWriter tekstVälja = new OutputStreamWriter(välja, "UTF-8")) {
  tekstVälja.write("hello world!");
}

Imestust võib tekitada asjaolu, et OutputStreamWriteri write meetod võtab väga mugavalt parameetriks sõne tüüpi muutuja, aga InputStreamReader puhul ei ole võimalik teksti mugavalt rea kaupa sisse lugeda. Tegelikult on see üsna loogiline. Nagu InputStream, nii ka InputStreamReader ei loe kogu voo sisu korraga sisse, vaid töötab sellega tükkhaaval. InputStreamReaderi ülesanne on seejuures baitide tähemärkideks dekodeerimine, mitte reavahetuste otsimine ja vahepeal läbi käidud teksti puhverdamine. Õnneks on olemas eraldi abiklass, mille ülesannete hulka kuulub tekstiridade leidmine.

Voogude puhverdamine

Tuleb välja, et kettalt (ja mujalt) baitide lugemine on tihti üllatavalt aeglane. Paljude väiksete andmetükikeste lugemise asemel oleks mõistlik lugeda korraga suurem tükk andmeid sisse, mis võimaldab operatioonisüsteemil kasutada erinevaid optimisatsioone. Sama kehtib ka kirjutamisega. Abiks on puhverdamise abiklassid BufferedInputStream ja BufferedOutputStream:

try (InputStream sisse = new BufferedInputStream(new FileInputStream("kassipilt.jpg"))) {
  // faili kasutamine
}

try (OutputStream välja = new BufferedOutputStream(new FileOutputStream("teine_kassipilt.jpg"))) {
  // faili kasutamine
}

Sisendvoo puhver hoiab endas vaikimisi 8KiB suurust baidimassiivi. Kui puhverdatud voost baite lugeda, siis puhver loeb enda sisemisse massiivi suure tüki andmeid ja annab neid vastavalt vajadusele välja. Niimoodi on puhverdatud voost isegi väikeste tükkide kaupa andmete küsimine kiire, sest ketta poole pöördutakse oluliselt harvem, kui seda ilma puhverdamata tehtaks. Analoogselt toimib see väljundvoogudega.

Puhverdamine on eriti kasulik tekstiliste andmete lugemise puhul. Teksti lugemiseks on olemas eraldi abiklass BufferedReader, mis lisaks koodi kiiremaks muutmisele oskab puhverdatud tekstist ka tekstiridu otsida:

try (InputStream baidid = new FileInputStream("sisend.txt");
     InputStreamReader tekst = new InputStreamReader(baidid, "UTF-8");
     BufferedReader puhverdatud = new BufferedReader(tekst)) {
  String rida = puhverdatud.readLine();
  while (rida != null) {
    System.out.println("lugesin voost: " + rida);
    rida = puhverdatud.readLine(); // loeb järgmise rea. kui ei saa, tagastab nulli
  }
}

Kõiki vahemuutujaid ei pea välja kirjutama:

try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("sisend.txt"), "UTF-8"))) {
  // faili kasutamine
}

Voogude nipid ja trikid

Voogudega saab erinevaid asju lugeda

Konsooli kasutamiseks saab kasutada Java standard sisend- ja väljundvoogu:

InputStream sisse = System.in;
OutputStream välja = System.out;

Internetist failide/veebilehtede lugemiseks saab kasutada URL klassi:

InputStream sisse = new URL("http://ut.ee/").openStream();

ZIP failidest saab pakitud failidega tegeleda ZipInputStream ja ZipOutputStream abil.

Abiklassid kasutavad abstraktsioone

Nii teksti kodeerimise kui ka puhverdamise klassid kasutavad InputStream ja OutputStream abstraktsioone. See võimaldab sama abiklassi erinevate baidivoogudega ja omavahel kasutada:

new InputStreamReader(new FileInputStream("kassipilt.jpg"));
new InputStreamReader(System.in);
new InputStreamReader(new URL("http://ut.ee/").openStream());

Voo lõpuni sisse lugemine

Mõnikord on mugavam või praktilisem andmeid töödelda ühes tükis, mitte voona (näiteks pildi ekraanil näitamisel või tabeli struktuuriga andmete töötlemisel). Kui andmed on kättesaadavad ainult voo kujul, on võimalik terve voo sisu korraga sisse lugeda. Seda peaks kaaluma ainult siis, kui on kindel, et voo sisu üldse mälusse ära mahub.

byte[] terveFail;
try (InputStream voog = new FileInputStream("kassipilt.jpg")) {
  terveFail = voog.readAllBytes();
}
// kasuta faili sisu

InputStream ja OutputStream ei ole ainsad sisend- ja väljundvood

Vaatasime klasse InputStreamReader ja OutputStreamWriter. Need on vastavalt abstraktsete klasside Reader ja Writer alamklassid. Reader ja Writer moodustavad täiesti eraldiseisvad sisend- ja väljundvoogude (märgivoogude) hierarhiad, mis spetsialiseeruvad tähemärkidega töötamisele. Sealt on pärit ka meile juba 4. praktikumist tuttav klass PrintWriter.

Ära unusta teksti kodeeringut määrata

Teksti kirjutamisel ja lugemisel määra alati kodeering! Kui kodeering määramata jätta, siis kasutatakse operatsiooni vaikekodeeringut, mis on igas arvutis erinev. Näiteks windows ei kasuta kunagi vaikekodeeringuna UTF-8, aga linux/mac enamasti kasutavad. Vaikekodeeringu kasutamise tulemusena võib juhtuda, et programm ei suuda iseenda kirjutatud faili lugeda, kui ta teises arvutis käivitada.

Voo "pikkus" ei ole määratud

Sisendvoog on struktuur, kust saab baite lugeda, kuni need võibolla kunagi otsa saavad. Sisendvool ei ole meetodit size ega length, millega saaks tuvastada allesjäänud baitide arvu, sest voog saab olla lõpmatu. Voogudel on olemas meetod available(), mille keerulisest dokumentatsioonist on kerge valesti aru saada. Voo pikkuse asemel näitab see voo sisemiste puhvrite hetkeseisu. Seda meetodit pole siin kursusel vaja.

Ülesanne 2

Kirjutage programm klassis Kaja, mis loeb konsoolist (st. standardvoost System.in) järjest tekstiridu ja prindib need tagasi välja, kuni programm kinni pannakse. Kasutage ülalmainitud abiklasse.

Huvilistele: ZipOutputStream kasutamine

Java standardteek sisaldab ka vahendeid failide kokku- ja lahtipakkimiseks.

Näide faili kokkupakkimisest zip-vormingusse:

import java.io.*;
import java.util.zip.*;

public class Zipper {
  public static void zipi(String pakitavFail, String zipFail) throws Exception {
    try (ZipOutputStream zipVäljund = new ZipOutputStream(new FileOutputStream(zipFail));
         FileInputStream sisendFail = new FileInputStream(pakitavFail)) {
      // ettevalmistus zip-faili väljastuseks
      zipVäljund.putNextEntry(new ZipEntry(pakitavFail));
      // sisendfailist lugemine ja zip-faili kirjutamine
      byte[] buf = new byte[1024]; // andmevahetusbuffer
      int len;
      while ((len = sisendFail.read(buf)) > 0) { // omistamine ja kontroll kombineeritud
        zipVäljund.write(buf, 0, len); // (andmed, nihe, pikkus)
      }
    }
  }
}

Ülesanne 3 (raskem)

Lisada eelmisele klassile meetod zip-vormingus faili lahtipakkimiseks. Luua testklass demonstreerimaks failide kokku- ja lahtipakkimist.

Juhis: abiks võiks olla klass ZipInputStream ja selle klassi meetod getNextEntry().

Mitte-tekstiline sisend-väljund

Kujutage ette kassaprogrammi, mis peab salvestama faili iga ostu info ja hiljem selle uuesti sisse lugema. Salvestame tühikutega eraldatult toote nime, koguse ja hinna ühe tekstireana faili. Hiljem loeme selle rea sisse ja jaotame käsitsi String.split ja Integer.parseInt abil tükkideks. Ülesanne on lihtne, aga koodi tuleb väga palju, sest split ja parseInt abil programmile tüüpide ja eraldajate selgeks tegemine on kohmakas. Parem oleks algusest peale andmed programmile selges formaadis salvestada ja pärast need otse õigete andmetüüpidega sisse lugeda. Sellega aitavad voogude abiklassid DataOutputStream ja DataInputStream.

Vaatame, kuidas saaks faili kirjutada järgnevate objektide sisu:

public class Toode {
  private String nimi;
  private double kogus;
  private double tükiHind;

  public Toode(String nimi, double kogus, double tükiHind) {
    this.nimi = nimi;
    this.kogus = kogus;
    this.tükiHind = tükiHind;
  }
}

public class Ostukorv {
  private String klient;
  private List<Toode> tooted;

  public Ostukorv(String klient, List<Toode> tooted) {
    this.klient = klient;
    this.tooted = tooted;
  }
}

Lõpptulemusena me tahaks teha midagi sellist:

public class OstukorviTest {
  public static void main(String[] args) throws Exception {
    Toode shokolaad = new Toode("shokolaad", 2.0, 1.29);
    Toode apelsin = new Toode("apelsin", 2.5, 0.89);
    Ostukorv ostukorv = new Ostukorv("Ats", Arrays.asList(shokolaad, apelsin));

    try (DataOutputStream dos = new DataOutputStream(new FileOutputStream("andmed.bin"))) {
      ostukorv.salvesta(dos);
    }

    try (DataInputStream dis = new DataInputStream(new FileInputStream("andmed.bin"))) {
      Ostukorv sisseLaetud = Ostukorv.laadi(dis); // samad andmed, mis algses
    }
  }
}

Andmete väljakirjutamine

Andmeid saab välja kirjutada klassiga DataOutputStream. Sellel on kõigi primitiivide ja sõne kirjutamise jaoks sobilikud meetodid. Lisame klassidesse Toode ja Ostukorv meetodi salvesta, mis kirjutab vastava klassi isendi kõik väljad DataOutputStreami abil voogu. Pange tähele, et Ostukorvis enne toodete salvestamist kirjutame välja ka toodete arvu. See aitab meid pärast Ostukorvi sisse lugemisel.

// class Toode 
public void salvesta(DataOutputStream dos) throws Exception {
  dos.writeUTF(nimi); // sisuliselt writeString
  dos.writeDouble(kogus);
  dos.writeDouble(tükiHind);
}

// class Ostukorv 
public void salvesta(DataOutputStream dos) throws Exception {
  dos.writeUTF(klient);
  dos.writeInt(tooted.size());
  for (Toode toode : tooted) {
    toode.salvesta(dos);
  }
}

DataOutputStreami abil kirjutatud andmed erinevad oluliselt OutputStreamWriteri kirjutatud andmetest. OutputStreamWriter võtab sisendiks sõned ja kirjutab voogu kodeeritult vastavad tähemärkide baidid. Näiteks arv 123456 salvestatakse baitidega { 49, 50, 51, 52, 53, 54 } ehk tähemärgid '1', '2', .., '6'. DataOutputStream see-eest kirjutab primitiivide väärtused välja nii, nagu need mälus hoitakse. Arv 123456 (int) kujutatakse mälus baitidega { 0, 1, 226, 64 } ja täpselt nii need ka voogu kirjutatakse. Mingit tähemärkideks teisendamist ega kodeerimist ei toimu.

Kõik primitiivid on Javas fikseeritud suurusega: int 4 baiti, long 8 baiti, float 4 baiti, double 8 baiti jne. DataOutputStream kirjutab iga tüüpi muutuja puhul alati vastava arvu baite voogu. DataOutputStreami writeUTF meetod oskab ka sõnesi kirjutada: kõigepealt kirjutatakse voogu short tüüpi väärtus (2 baiti), mis näitab sõne pikkust ja seejärel kirjutatakse sõne UTF-8 kodeeritud baidid.

Kuna DataOutputStreami kirjutatud andmed on alati selge struktuuriga (iga tüüp on kindla suurusega), muutub selle abil kirjutatud andmete lugemine üsna lihtsalt. DataInputStream pakub meetodeid voost erinevate primitiivide ja sõnede lugemiseks. Selle lihtsustamiseks saab ta eeldada, et iga tüüpi väärtust lugedes on vaja voost võtta täpselt nii mitu baiti, kui palju vastav primitiiv mälus ruumi võtab. Näiteks voost täisarvu (int) ja ujukomaarvu (double) lugemiseks tuleb lugeda kõigepealt 4 baiti (need moodustavad täisarvu) ja siis veel 8 baiti (ujukomaarv). InputStreamReaderit kasutades peaks lugema ettearvamatu koguse tähemärke, kuni leiame mingi arve eraldava tähemärgi (tühik?) ja seejärel parseInt ja parseDouble meetodeid kasutama.

Andmete sisse lugemine

Nüüd kirjutame koodi, mis suudab salvestatud andmed tagasi sisse lugeda. Selle juures on ainus nõue lugeda sisse andmed täpselt samas järjekorras, mis nad välja kirjutati ja kasutada täpselt samu andmetüüpe.

// class Toode
public static Toode laadi(DataInputStream dis) throws Exception {
  String nimi = dis.readUTF();
  double kogus = dis.readDouble();
  double tükiHind = dis.readDouble();
  return new Toode(nimi, kogus, tükiHind);
}

// class Ostukorv
public static Ostukorv laadi(DataInputStream dis) throws Exception {
  String klient = dis.readUTF();
  List<Toode> tooted = new ArrayList<>();
  int tooteid = dis.readInt(); // ära pane for tsükli tingimusse
  for (int i = 0; i < tooteid; i++) { 
    tooted.add(Toode.laadi(dis));
  }
  return new Ostukorv(klient, tooted);
}

Link lõpplahenduse tervele koodile: https://github.com/mbakhoff/oop-samples/tree/master/streams/sample1

Millal kasutada DataOutputStreami ja millal OutputStreamWriterit? DataOutputStream on hea siis, kui pärast on programmil vaja samad andmed sisse lugeda ja neid struktureeritud kujul hoidma ja töötlema hakata (näiteks objektides salvestada). OutputStreamWriter on hea siis, kui kirjutatav fail peab olema inimesele mugavalt loetav, käsitsi tekstiredigeerijas muudetav ja selle koodis töötlemise mugavus on vähem oluline.

Ülesanne 4 (kontroll)

Kirjutada klass Tunniplaan, mille ühele konstruktorile on võimalik ette anda kaks massiivi: ained (String[]) ja algused (int[]). Alguste massiivi iga element on vastava indeksiga aine algusaeg, arvestatud minutitena päeva algusest (nt {10*60+15, 12*60+15}). Meetod int annaAlgusaeg(String aine) peab tagastama argumendina antud aine algusaja.

Klassis peab olema ka meetod kirjutaFaili(String failinimi), mis salvestab ained ja algusajad DataOutputStream-i abil näidatud faili. Lõpuks lisa klassile alternatiivne konstruktor, mis massiivide asemel võtab argumendiks failinime, kust massiivid sisse lugeda.

Peaklassis demonstreerida Tunniplaani konstruktorite ja meetodite korrektset toimimist.

Vihje: Faili sisse lugemisel on mugav teada, mitu aine-algusaeg paari failis on. Sisselugemise lihtsustamiseks võiks paaride arvu faili algusesse kirjutada.

Ülesanne 5 (kontroll)

Luua programm, mis etteantud tekstifaili ja sõne korral leiab sõne esinemiste arvu failis.

Ülesanne lahendada klassi BufferedReader ja selle readLine meetodi abil (näide peatüki keskel tekstis). Lahendus oleks võimalik koostada ka juba tuttava Scanner klassi abil, aga harjutamise eesmärgil kasutada uusi vahendeid.

Automaattestimise võimaldamiseks lepime kokku, et programm peab asuma klassis TekstiAnalüsaator, mida peab olema võimalik kasutada sedasi:

 TekstiAnalüsaator analüsaator = new TekstiAnalüsaator("kiri.txt");
 int esinemisteArv = analüsaator.sõneEsinemisteArv("kala");

Ülesanne 6

Kirjutada programm, mis suunab klaviatuurisisendi tekstifaili.

Ülesande lahendamisel kasutada sisendi lugemiseks BufferedReader klassi ja faili kirjutamiseks FileOutputStream klassi. Lahendus oleks võimalik koostada ka juba tuttava PrintWriter klassi abil, aga harjutamise eesmärgil kasutada uusi vahendeid.

Vihje: Abiks on FileOutputStream meetod write(byte[]) ja String meetod getBytes(charsetName).

Ülesanne 7

Kirjutada programm, mis klaviatuurilt sisestatud ja ekraanile väljastatavas tekstivoos asendab alamsõne "x" kõik esinemised alamsõnega "ks".

Ülesanne 8 (kontroll)

Koostada programm, millele antakse käsurealt (Juhend käsurea parameetrite määramiseks) ette "keelatud" sõnad. Kui programm on käivitunud, peab olema võimalik programmi standardsisendisse saata erinevaid sõnu, iga sõna eraldi real. Need sõnad, mis pole keelatud sõnade hulgas, peab programm kuvama standardväljundisse, iga sõna eraldi real. Programmi töö peab lõppema, kui sisestati tühi rida.

Lisaülesanne: lõpeta programmi töö ka siis, kui standardsisend saab tühjaks. Käsureal programmi testides saab sisendi lõppu tekitada klahvikombinatsiooniga Ctrl-D (Linux ja Mac) või Ctrl-Z (Windows).

Programm peab asuma klassis nimega Tsensuur.

Ülesanne 9 (kontroll)

Muusikapala žanri näitab mp3-faili viimane bait. Näiteks kantri puhul on selle väärtus 2 ja metali puhul 9. Kirjutada klass Mp3Analüsaator, mis võtab argumendiks failinime ja millel on boolean-tüüpi meetod onKantri, mis kontrollib, kas etteantud mp3-failis on kantrilugu. Kasuks tulevad meetodid seek, length ning readByte klassist RandomAccessFile. Peameetodis katsetada meetodit.

Ülesanne 10

Loengus vaadeldi ka veebilehtede sisu avamist URL ja URLConnection klasside abil. Katsetada ka neid võimalusi vähemalt elementaarsel tasemel. Ühtki konkreetset ülesannet ei ole, aga tuleb valmis olla, et neid võimalusi tuleb rakendada 2. kontrolltöös.

  • Arvutiteaduse instituut
  • Loodus- ja täppisteaduste valdkond
  • Tartu Ülikool
Tehniliste probleemide või küsimuste korral kirjuta:

Kursuse sisu ja korralduslike küsimustega pöörduge kursuse korraldajate poole.
Õppematerjalide varalised autoriõigused kuuluvad Tartu Ülikoolile. Õppematerjalide kasutamine on lubatud autoriõiguse seaduses ettenähtud teose vaba kasutamise eesmärkidel ja tingimustel. Õppematerjalide kasutamisel on kasutaja kohustatud viitama õppematerjalide autorile.
Õppematerjalide kasutamine muudel eesmärkidel on lubatud ainult Tartu Ülikooli eelneval kirjalikul nõusolekul.
Courses’i keskkonna kasutustingimused