Forklaring til programmet FilTest.java

Like dokumenter
Hittil har programmene kommunisert med omverden via tastatur og skjerm Ønskelig at data kan leve fra en kjøring til neste

public class NaivRiking { private HeldigSnylter minsnylter; public NaivRiking(HeldigSnylter h) { minsnylter = h;

IN Notat om I/O i Java

Forklaring til programmet AbstraktKontoTest.java med tilhørende filer Konto.java, KredittKonto.java, SpareKonto.java

Bruk av class Scanner, FileWriter og Formatter som alternativ til EasyIO i INF1000.

2 Om statiske variable/konstanter og statiske metoder.

INF1000-SIKT - Notat om I/O i Java

INF Notat om I/O i Java

INF1010 våren 2019 Onsdag 30. januar. Mer om unntak i Java (med litt repetisjon av I/O først)

Litt om pakker og mest om data inn og ut

TDT4110 Informasjonsteknologi grunnkurs: Kapittel 7 Filer og unntak ( exceptions ) Professor Alf Inge Wang Stipendiat Lars Bungum

Leksjon 7. Filer og unntak

Bruk av class Scanner, split(), FileWriter og Formatter som alternativ til easyio i INF1000.

2 Om statiske variable/konstanter og statiske metoder.

Forelesning inf Java 4

INF1000 Behandling av tekster

i=0 Repetisjon: arrayer Forelesning inf Java 4 Repetisjon: nesting av løkker Repetisjon: nesting av løkker 0*0 0*2 0*3 0*1 0*4

Å lese tall fra en fil, klassen Scanner

Det du skal gjøre i denne oppgava er først å sette opp bakgrunnen til spillet og så rett og slett å få firkanter til å falle over skjermen.

Plan for dagen. Vprg 4. Dagens tema - filbehandling! Strømmer. Klassen FilLeser.java. Tekstfiler

Kort om meg. INF1000 Uke 2. Oversikt. Repetisjon - Introduksjon

Oversikt. INF1000 Uke 6. Objekter, pekere og null. Lese og skrive fra/til fil. Litt om objekter, pekere og null Filer og easyio. Litt mer om tekster

3 emner i dag! INF1000 Uke 5. Objekter og pekere. null. Litt om objekter, pekere og null Filer og easyio Litt mer om tekster

Tetris. Introduksjon. Skrevet av: Kine Gjerstad Eide. Lag starten på ditt eget tetris spill!

INF1010 våren 2017 Onsdag 25. januar. Litt om unntak i Java

Oversikt. INF1000 Uke 1 time 2. Repetisjon - Introduksjon. Repetisjon - Program

Fra Python til Java, del 2

Eksekveringsrekkefølgen (del 1) Oppgave 1. Eksekveringsrekkefølgen (del 2) Kommentar til oppgave 1. } // class Bolighus

INF1010 våren 2018 tirsdag 23. januar

TOD063 Datastrukturer og algoritmer

Innhold uke 4. INF 1000 høsten 2011 Uke 4: 13. september. Deklarasjon av peker og opprettelse av arrayobjektet. Representasjon av array i Java

Post-it spørsmål fra timen (Arv og subklasser)

LITT OM OPPLEGGET. INF1000 EKSTRATILBUD Stoff fra uke September 2012 Siri Moe Jensen EKSEMPLER

INF1000 (Uke 5) Mer om løkker, arrayer og metoder

INF1000 EKSTRATILBUD. Stoff fra uke 1-5 (6) 3. oktober 2012 Siri Moe Jensen

Forelesning inf Java 5

Forkurs INF1010. Dag 3. Andreas Færøvig Olsen Eivind Storm Aarnæs

Forelesning inf Java 5

Lese fra fil. INF1000 : Forelesning 5. Eksempel. De vanligste lesemetodene. Metoder:

Forkurs INF1010. Dag 3. Andreas Færøvig Olsen Gard Inge Rosvold Institutt for Informatikk, 15.

INF1000 : Forelesning 4

Algoritmer og datastrukturer Vedlegg A.2 BitOutputStream

Av Stein Gjessing, Institutt for informatikk, Universitetet i Oslo

i=0 i=1 Repetisjon: nesting av løkker INF1000 : Forelesning 4 Repetisjon: nesting av løkker Repetisjon: nesting av løkker j=0 j=1 j=2 j=3 j=4

programeksempel Et større En større problemstilling Plan for forelesingen Problemstillingen (en tekstfil) inneholdt ordet "TGA"

UNIVERSITETET I OSLO

Oversikt. INF1000 Uke 2. Repetisjon - Program. Repetisjon - Introduksjon

Løsningsforslag ukeoppg. 6: 28. sep - 4. okt (INF Høst 2011)

Forelesningsquiz. Forelesning inf Java 5. Sett dere to (eller tre) sammen og besvar de fire spørsmålene på utdelt ark. Tid: 15 min.

Verden - Del 2. Steg 0: Oppsummering fra introduksjonsoppgaven. Intro

UNIVERSITETET I OSLO

Spørsmål fra forrige forelesning. INF1000 Forelesning 7. Oppførselen til inword()/inint()/etc. Operator-presedens i Java

Enkel lesing og skriving i Java

Algoritmer og datastrukturer A.1 BitInputStream

Læringsmål for forelesningen

UNIVERSITETET I OSLO

Ta inn og ut av 2D-array. Java 6. Liste over ulike verdier i 2D-array. Det ferdige programmet. Vi skal lage et program som illustrerer hvordan man

Beskrivelse av programmeringsspråket Compila15 INF Kompilatorteknikk Våren 2015

INF 1000 høsten 2011 Uke september

INF1000 undervisningen INF 1000 høsten 2011 Uke september

INF Uke 10. Ukesoppgaver oktober 2012

Hvis en person har inntekt < , så betaler han 10% skatt på alt, og ellers betaler han 10% skatt på de første og 30% på resten.

Hvis en person har inntekt < , så betaler han 10% skatt på alt, og ellers betaler han 10% skatt på de første og 30% på resten.

Algoritmer og datastrukturer A.1 Filbehandling på bit-nivå

Python: Filer og unntak Gaddis: Kapittel 6

Oblig 4Hybelhus litt mer tips enn i oppgaven

TDT4102 Prosedyre og Objektorientert programmering Vår 2014

UNIVERSITETET I OSLO

Informasjon Prøveeksamen i IN1000 høsten 2018

UNIVERSITETET I OSLO

Tre måter å lese fra terminal. Java 4. Eksempel. Formatert utskrift til skjerm

Algoritmer og datastrukturer E Løkker i Java

1- og 2-veis Innkapsling Java Stabel Kø Prio-kø Iterator. Enveis- og toveislister Innkapsling («boxing») (Big Java 6.8.5)

Programmering Høst 2017

INF1000 (Uke 15) Eksamen V 04

INF1000 (Uke 15) Eksamen V 04

Jentetreff INF1000 Debugging i Java

IN1010. Fra Python til Java. En introduksjon til programmeringsspråkenes verden Dag Langmyhr

Kort repetisjon av doble (nestede) løkker Mer om 1D-arrayer Introduksjon til 2D-arrayer Metoder

INF1000 (Uke 6) Mer om metoder, tekster

EKSAMENSFORSIDE Skriftlig eksamen med tilsyn

Rep: Metoder. INF1000 (Uke 6) Mer om metoder, tekster. Rep: Metoder. 3 typer variable: Klassevariable. Java-programmene så langt i kurset:

Kapittel 1 En oversikt over C-språket

INF1000 : Forelesning 5

Dagens tema: 12 gode råd for en kompilatorskriver

Leksjon 7. Filer og unntak

Array&ArrayList Lagring Liste Klasseparametre Arrayliste Testing Lenkelister

Enkle generiske klasser i Java

EKSAMEN 6108/6108N PROGRAMMERING I JAVA Alt trykt og skriftlig materiale.

Python: Variable og beregninger, input og utskrift. TDT4110 IT Grunnkurs Professor Guttorm Sindre

INF1000 Metoder. Marit Nybakken 16. februar 2004

løsningsforslag-uke5.txt

Algoritmer og datastrukturer Vedlegg A.4 Filbehandling på char-nivå

b) 10 2 = 20 c) 5 1 = 5.

Programmering i C++ Løsningsforslag Eksamen høsten 2005

1- og 2-veis Innkapsling Java Stabel Kø Prio-kø Iterator. Enveis- og toveislister Innkapsling («boxing») (Big Java 6.8.5)

INF1000 HashMap. Marit Nybakken 2. november 2003

Repetisjon: operatorene ++ og -- Java 5. Nøtt. Oppgave 1 (fra forrige gang) 0 udefinert udefinert. Alternativ 1 Prefiks-operator

Hva er verdien til variabelen j etter at følgende kode er utført? int i, j; i = 5; j = 10; while ( i < j ) { i = i + 2; j = j - 1; }

Eksamensoppgave i IFUD1025 Programmering i Java og IINI4013 Programmering i Java

Transkript:

Forklaring til programmet FilTest.java Programmet FilTest.java illustrerer lesing fra fil og skriving til fil, samt hvordan man kan legge data fra filen inn i en ArrayList. Man kunne også ha brukt et vanlig array her, men hvis antall linjer i filen er ukjent og potensielt kan være veldig stort, har ArrayList den fordelen at den kan øke i takt med behovet, mens en array [] må deklareres med fast størrelse. Programmet FilTest.java består kun av en main()-metode, og det gjør ikke noe særlig fornuftig, er bare ment å illustrere hvordan enkel filbehandling gjøres. Oppgaven kan tenkes å være følgende: Vi har en fil som inneholder tall, denne fila er inndelt i linjer. Vi ønsker et program som leser denne fila, og så skriver en utfil, hvor hver linje inneholder tallet som er summen av tallene på tilsvarende linje i innfila. Hvis man bruker fila innfil.txt som følger med eksemplet, og som inneholder 2 3 45 67 89 2 5 9 45 32 54 1 7 8 2 21 32 43 65 1 2 1 2 1 2 1 87 76 65 54 1 skal utskriften tilsvarende bli 222 149 164 7 283 Vi vil nå forklare steg for steg hvordan dette oppnås: For å ta vare på summene som vi regner ut, opprettes en ArrayList med ArrayList tallliste = new ArrayList(); Som man ser av denne deklarasjonen, i motsetning til en tilsvarende array-deklarasjon (som f.eks. kunne ha vært int[] tallliste = new int[1000]; sies det for ArrayList verken noe om størrelsen på lista (mens arrayet eksplisitt har 1000 elementer) eller typen på elementene den skal inneholde (mens arrayet inneholder int, dvs. heltall). En ArrayList inneholder imidlertid objekter, ikke data av primitive typer, så dette innebærer at vi må ty til visse ekstratriks som vil vise seg etter hvert. Så går man inn i en try-blokk. Dette gjøres fordi flere av de påfølgende metodekallene kan komme til å kaste unntak som gjør at vi ikke kan fortsette på normal måte, f.eks. at programmet ikke finner den fila vi ønsker å lese fra. Heller enn å skulle henvende seg til fila direkte, oppretter vi et objekt av klassen FileReader med deklarasjonen FileReader filleser = new FileReader( innfil.txt ); Her er innfil.txt navnet på fila som skal leses, så for at programmet skal virke må denne fila finnes på samme katalog som programfila selv. Hvis dette ikke er tilfelle, vil det her bli kastet en FileNotFoundException, slik at programutførelsen hopper ut av try-blokka og i stedet gir utskriften Filen ikke funnet, som spesifisert i første catch. Ved hjelp av metoder i klassen FileReader kunne vi nå lese ett og ett tegn fra filen. Dette er imidlertid noe tungvint, så vi velger heller å pakke objektet filleser inn i et nytt og mer overordnet objekt: BufferedReader leser = new BufferedReader(filLeser); Dette gir oss et mer avansert leser-objekt som er i stand til å lese data fra filen i større jafs, og som blant annet tilbyr metoden readline() som leser en linje av gangen, dvs. leser fram til neste linjeslutt (EOL), som er et spesielt tegn i tegnsettet. Med setningen

String enlinje = leser.readline(); leses altså den første linjen i filen, og denne puttes inn i en strengvariabel, enlinje. For å få lest alle linjene i filen brukes nå en løkkekonstruksjon while (enlinje!= null) {... enlinje = leser.readline(); } hvor vi nederst i løkka leser neste linje. Grunnen til at vi måtte lese den aller første linja før løkka, er at vi ellers ikke ville hatt noen fornuftig test for while på den aller første runden i et generelt tilfelle kan det jo hende at fila er helt tom, slik at while-testen må kunne feile også aller første gang. Selve testen er som den er fordi metoden readline() i BufferedReader returnerer verdien null (dvs intet) når den ikke finner flere linjer men i stedet kommer til slutten av fila. Dette vil være representert ved et spesielt tegn EOF, end of file, som ikke er synlig når vi ser fila på skjermen f.eks. i emacs eller NotePad, eller dvs. egentlig et tall (siden alle data på disken til syvende og sist er tall) som betyr end-of-file i det aktuelle tegnsettet. Tilsvarende vil det på slutten av hver linje finnes et spesialtegn EOL, end of line, som heller ikke er synlig når man ser på fila på vanlig måte. Det er disse tegnene readline()-metoden forholder seg til når den klarer å pelle ut en og en linje som streng fra fila. Hvis et eller annet skulle skjære seg underveis i lesing av fila, vil det kunne bli kastet et unntak (IOException). Dette vil på samme måte som FileNotFoundException nevnt tidligere føre til at utførelsen av try-blokka avbrytes og man i stedet utfører den catch-setningen som passer med unntakets type. Spørsmålet blir så hva vi skal gjøre inni den ovennevnte løkka. Problemet vi nå står overfor er at vi har en streng men egentlig ønsker diverse tall. (På første runde, hvis man bruker eksempelfila innfil.txt, vil dette være strengen 2 3 45 67 89 2 5 9. Her kunne vi selvsagt skrive noen programlinjer for å lete gjennom strengen tegn for tegn og se hvor det står tall og hvor det står blanke, og så lage tall utifra dette. Imidlertid fins det en klasse i javabiblioteket som kan gjøre dette for oss så vi selv slipper å skrive kode for å lete gjennom strengen, nemlig StringTokenizer. Denne kan ta en streng og dele den opp i mindre bestanddeler (s.k. tokens) ut ifra vanlige skilletegn (som f.eks mellomrom, komma, semikolon). Hvis man ønsker å bruke andre skilletegn (f.eks. < > ifbm lesing av HTML-filer), kan dette spesifiseres i et ekstra argument til konstruktoren, jfr. API. StringTokenizer tekst = new StringTokenizer(enLinje); lager altså et objekt tekst som igjen har en referanse til objektet enlinje. Tokenizer-objektet skal behandle linja for oss og returnere hensiktsmessige data. Dessuten trenger vi en enkel intvariabel for å ta vare på summen som vi regner ut etter hvert, denne deklareres med int linjesum = 0; Nå kan vi gjøre bruk av to nyttige metoder i StringTokenizer, nemlig hasmoretokens() som returnerer boolean (true/false) alt etter om strengen inneholdt flere tokens (dvs i dette tilfellet flere tall) eller ikke. Metoden nexttoken() returnerer String og kan brukes til å hente neste token hvis det var flere. Gitt strengen fra forrige avsnitt vil denne returnere en ny substreng av denne for hvert gjennomløp, først 2, så 3, så 45 etc. Ved hjelp av while (tekst.hasmoretokens()) { String s = tekst.nexttoken();...} skaffer vi oss altså strenger som bare inneholder de relevante tallene. Disse må så gjøres om til heltall for at vi enkelt skal kunne regne med dem. Dette gjøres ved hjelp av en statisk metode parseint() som fins i wrapper-klassen Integer (som generelt brukes i tilfeller hvor man

trenger å representere en heltallsverdi som et objekt se f.eks. hvordan dette kommer til hjelp lenger nede i programmet når tall skal legges inn i en ArrayList). ParseInt() tar en streng som parameter og returnerer det tilsvarende heltallet. Siden metoden er statisk, trenger vi ingen spesiell instans av Integer for å kunne kalle den, men kan aksessere den direkte via klassenavnet Integer. int tall = Integer.parseInt(s); vil altså gjøre strengen om til et heltall, dvs. 2 vil bli 2, 3 vil bli 3, 45 vil bli 45, etc. Hvis det skulle stå noe på fila som ikke blir et heltall, f.eks. 3.45 eller 2A, vil det bli kastet et unntak (NumberFormatException). Som man vil se av programkoden, blir dette unntaket ikke fanget (det fins ingen catch for NumberFormatException). Likevel får man ingen kompileringsfeil med denne koden. Grunnen til dette er at NumberFormatException tilhører en gruppe unntak som man ikke er nødt til å fange, da den er indirekte subklasse av RunTimeException (kjøretidsunntak). Hvis det hadde stått noe på fila som ikke var heltall, ville brukeren her få en noe kryptisk melding på skjermen (om at NumberFormatException oppsto i linje sånn og sånn). For å gjøre programmet mer brukervennlig er det derfor anbefalt å fange også denne typen unntak. For å prøve ut forskjellen, kan du gjøre følgende (gitt at du har kopiert FilTest.java og innfil.txt til din bruker og kompilert java-filen): Endre innfil.txt slik at den også inneholder noe som ikke er heltall (f.eks desimaltall eller bokstaver) Kjør programmet og se hva som skjer. Endre programmet ved å legge inn en catch også for NumberFormatException, med en mer forståelig feilmelding Kjør programmet på nytt og se forskjellen For så å gå videre med forklaringen: linjesum += tall; betyr som vanlig det samme som linjesum = linjesum + tall; dvs. det nye tallet som ble funnet blir addert inn i linjesummen. Dette avslutter den indre while-løkka, og innebærer altså at når denne har gått seg ferdig (ikke flere tokens) så er alle tallene på en og samme linje plusset inn i denne summen. Utifra dette kan man også forklare hvorfor linjesum = 0; måtte stå inni den ytterste while-løkka, men utenfor den innerste while-løkka: Hadde den stått inni den innerste while-løkka, ville summen ha blitt satt til 0 for hvert eneste tall som ble funnet, slik at vi ikke hadde fått summert opp noe som helst Hadde den stått foran den ytterste while-løkka, ville summen kun ha blitt nullstilt helt i starten av programmet. Når vi begynte på linje 2, ville vi da bære med oss summen fra linje 1, osv., dvs kun summen for linje 1 ville bli riktig. Med denne innerste while-løkka har vi nå fått summert tallene for en linje (først for den første linja i fila, der summen blir 222 som vist over. Vi trenger nå å huske denne summen på en annen måte enn i variabelen linjesum, som jo vil få nye verdier summert inn i seg når vi kommer til neste runde av den ytre løkka. Siden det i et generelt tilfelle kan være et uvisst antall linjer på fila, bruker vi ArrayList heller enn et mindre fleksibelt array med int[]. Siden ArrayList imidlertid kun kan inneholde objekter, ikke primitive verdier som sådan, må vi gjøre om heltallet vårt til et objekt. Her kommer wrapper-klassen Integer inn i bildet, med denne kan vi konstruere et objekt hvis eneste attributt nettopp er en int-verdi (se API for mer detaljer om Integer og evt andre wrapper-klasser som Double, Char etc). Dette gjøres med Integer sum = new Integer(linjeSum); Som vi ser gis linjesum inn som argument til konstruktoren, dette vil dermed bli objektets attributtverdi. Denne setningen måtte stå nedenfor den indre while-løkka, ellers ville vi ha kommet til å lage like mange Integer-objekt som det var tall på innfil.txt heller enn bare ett Integer-objekt for summen av hver linje. Det som nå gjenstår, er å legge Integer-objektet inn i

vår ArrayList, dette gjøres med tallliste.add(sum); -- hvor add() er en metode som klassen ArrayList tilbyr, og som setter inn det nye elementet bakerst i lista (ArrayList tilbyr også innsetting andre steder enn bakerst, på en langt enklere måte enn et vanlig array, man kan da bruke add(objekt,posisjon) hvor tallet posisjon vil angi hvor det nye elementet skal settes inn. Andre elementer vil da automatisk bli skjøvet bakover i lista, mens man for et vanlig array må ordne dette selv. Den siste kodelinja, enlinje = leser.readline(), leser som forklart tidligere en ny linje fra fila innfil.txt. Dette vil fortsette så lenge det fins linjer å lese, dvs. inntil man kommer til filslutttegnet som alltid vil befinne seg bakerst i en fil. Nedenfor ytre løkke utfører vi leser.close(); som forteller programmet at vi nå gir slipp på denne filen, slik at andre kan bruke den i stedet. Så kommer catch-setningene, disse vil som nevnt tidligere slå til dersom try-blokka blir avbrutt av et unntak. Hvis unntaket er at fil ikke er funnet, vil den første slå til, andre mulige feil (bortsett fra den nevnte NumberFormatException) vil dekkes av den andre. Så kommer en ny try-blokk, med den delen av programmet som skal skrive utfilen. (I og for seg kunne lesing og skriving ha vært gjort i samme løkke, som ville ha gitt oss et litt mindre program og hvor vi hadde sluppet å huske summene midlertidig i en ArrayList fordi vi kunne skrive dem rett på fila. Av pedagogiske grunner fant jeg det imidlertid hensiktsmessig å skille skriving og lesing fra hverandre i dette første eksemplet slik at man lett skulle se hva som var hva. Et alternativt program som gjør alt i samme løkke kan ses på AlternativFilTest.java.) Her starter vi med FileWriter fw = new FileWriter( utfil.txt ); Hvis utfil.txt ikke eksisterer fra før, vil denne sørge for at den opprettes mens FileReader som vi så tidligere da ville kaste unntak. Vi kunne nå ha brukt dette fw-objektet direkte for å skrive til fila, men det ville være tungvint, da den kun tilbyr metoder som skriver en og en char. Vi pakker den derfor inn med BufferedWriter bw = new BufferedWriter(fw); for å få bufring, dvs. kunne lese dataene i større jafs fra fila, og denne pakkes igjen inn med PrintWriter pw = new PrintWriter(bw); da det mhp utskrift er klassen PrintWriter som tilbyr den metoden det her er hensiktsmessig å bruke, nemlig println() (som funker akkurat som System.out.println(), bare at vi her skriver til noe annet enn skjermen). Løkka for (int i = 0; i<tallliste.size(); i++) {...} vil bli utført like mange ganger som det fins elementer i ArrayList tallliste (da size()-metoden nettopp gir antall elementer i en slik liste). Setningen int tall = ((Integer)tallListe.get(i)).intValue(); inneholder diverse poenger på en gang og kan dermed trenge en mer inngående forklaring. På venstre side av = har vi en enkel int-variabel. For at tilordningen skal funke er vi dermed avhengig av å få en int ut av høyre side også. Hvordan dette skjer kan forklares innenfra og ut: Metoden tallliste.get(i) gir oss det elementet i listen som har indeks i. Som for vanlige array starter indeks i en ArrayList på 0, så første gang vil kallet være get(0), som nettopp vil gi det fremste elementet i listen. Det vi får returnert fra get(i) er imidlertid et objekt av den helt generelle typen Object. Grunnen til dette er at en ArrayList i prinsippet kan inneholde hvilke objekter som helst (som man kan huske fra starten av programmet, ble det ikke sagt noe om innholdets type i deklarasjonen av lista). Dette er nyttig mhp fleksibilitet, men et problem her når vi egentlig bare ønsker å få tak i den intverdien som vi i utgangspunktet la inn. Klassen Integer er i stand til å svare på

spørsmål om hvilken int-verdi den inneholder gjennom metoden intvalue(). Men klassen Object har ingen slik metode. Så hvis vi ganske enkelt skriver tallliste.get(i).intvalue() vil vi få kompileringsfeil selv om vi vet at det vi la inn i lista vår tidligere var objekter av typen Integer, så vet ikke kompilatoren dette, den forholder seg til at metoden get() som brukes her, er spesifisert med returtype Object, og at Object ikke har noen metode intvalue(). For å kunne benytte intvalue()-metoden må vi derfor først gjøre en s.k. cast, som man her best kunne oversette til norsk som avsløring : vi må si til kompilatoren at ok, get() returnerer Object, men vi vet at det faktiske objektet som vi her får referanse til egentlig er av den mer spesifikke typen Integer. Dette gjøres med ((Integer)tallListe.get(i)) hvor den innerste parentesen rundt Integer forteller at vi her gjør en cast til Integer, og den ytterste parentesen forteller at denne casten gjelder for hele uttrykket tallliste.get(i), dvs. det objektet som get()-kallet gir som returverdi. Uten denne ytre parentesen ville man ha forsøkt å caste bare tallliste til Integer, hvilket ville ha gått svært dårlig. Ved hjelp av ovennevnte cast har vi nå fått et objekt av typen Integer og er i stand til å bruke metoden intvalue() på dette. Merk at dette metodekallet står bak den ytre parentesen for casten nevnt over, da man her nettopp er avhengig av å forholde seg til et Integer-objekt. intvalue() returnerer int, og dermed er vi i mål med et resultat som kan tilordnes variabelen på venstre side. Når verdien for variabelen tall er funnet, er det en enkel sak å skrive den til fila. pw.println(tall); skriver tallet på fila, deretter et linjeskift, slik at neste tall vil komme på ny linje. Hadde vi i stedet skrevet pw.print(tall) ville neste tall ha kommet like bak dette. Hvis man ønsker å skrive flere tall på samme linje, må man evt sette inn skilletegn mellom dem, f.eks pw.print(tall + ); -- igjen kan man se at print-setningene virker på samme måte som for System.out. Når alle elementer i vår ArrayList er gjennomgått, vil for-løkka avsluttes og vi lukker fila med pw.close(); catch-blokka med IOException vil slå til hvis et eller annet går galt under skriving til fila. Hvis intet går galt hopper man over catch-blokka, og programmet er ferdig. Som en siste kommentar kan det nevnes at opprettelsen av FileReader og FileWriter gjør i ett trinn noe vi også kunne ha gjort i to trinn: File innfil = new File( innfil.txt ); FileReader filleser = new FileReader(innfil);... File utfil = new File( utfil.txt ); FileWriter fw = new FileWriter(utfil); Normalt vil man kanskje foretrekke måten som er brukt i programmet, fordi den gir mindre skriving (denne vil implisitt opprette filobjekter som igjen er pakket inn i respektive reader og writer-strøm). Men bruk av navngitte File-objekter som vist like over her kan også ha fordeler, fordi man da har eksplisitt tilgang til noen nyttige metoder som File-klassen tilbyr.

Hvis man ønsker å gjøre direkte oppslag på gitte posisjoner i en fil, må man opprette et RandomAccessFile objekt (jfr API), da bruk av Reader- og Writer-strømmer (eller andre slags strømmer) kun vil gi sekvensiell aksess, dvs. gjennomgang av strømmen fra start til slutt. Et annet poeng er at man alternativt kunne ha gjort hele opprettelsen av innstrøm og utstrøm, hhv., med en enkelt tilordning. Siden vi videre i programmet kun henvender oss til den ytterste innpakningen (BufferedReader og PrintWriter, hhv.), er det kun disse to vi trenger eksplisitte objektreferanser til de øvrige kan godt være anonyme. Med denne varianten ville vi putte konstruktorene inni hverandre i flere nivåer, som direkte viser innpakningen: //lesestrøm BufferedReader leser = new BufferedReader(new FileReader( innfil.txt )); //skrivestrøm PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter( utfil.txt ))); Hva skjer bak kulissene? Denne delen er ikke nødvendig å lese hvis man har skjønt det over og er fornøyd med å vite bare hvordan programmet virker, men enkelte kan muligens syns at det også er greit å vite hva som skjer bak kulissene når vi her har pakket filene inn i diverse objekter som skal gjøre deler av jobben for oss. Det kan derfor være interessant å vise objektstrukturen og hvordan de metodekallene som fins i programmet forplanter seg til metoder implementert i de standardklassene vi har gjort bruk av. Lesing: Her oppretter vi først en FileReader filleser som pakker inn selve filen, deretter en BufferedReader leser som pakker inn det forannevnte filleser-objektet. På figuren er objektreferansene (som deklareres på venstre side av tilordningene) vist som rektangler, og selve objektene vist som ovaler: leser filleser Selve filen Selve leserobjektet Selve filleserobjektet Main-metoden kjenner både referansene leser og filleser og ville dermed være i stand til å kalle metoder hos begge disse objektene. FilLeser-referansen brukes imidlertid kun som argument til konstruktoren for leser-objektet, deretter gjøres kun metodekall til leser-objektet (som nå kjenner til filleser-objektet via en private attributt som ble satt på basis av argumentet som ble gitt inn til konstruktoren, akkurat som et Kunde-objekt kan kjenne til sitt Konto-objekt via en attributt minkonto). Handlingsgangen blir altså: main() kaller leser.readline() under utførelsen av sin readline()-metode, vil leser-objektet gjøre kall (gjerne flere) til filleser.read(), disse kallene ser vi ikke i vår kode fordi vi har importert BufferedReader-klassen heller enn å programmere den selv

i sin read()-metode vil filleser-objektet igjen utføre kode som gjør selve lesingen fra fil, og read()-metoden vil returnere tegnene som ble funnet på filen til den kallende readline()-metoden. readline()-metoden vil så sette sammen tegn til en streng som utgjør en hel linje og returnere denne strengen til main()-metoden (hvor den tilordnes variabelen String enlinje) Et tilsvarende opplegg vil vi finne for skriving, hvor vi har enda et nivå med innpakning. Figuren ville ellers bli helt lik den ovenfor, så jeg bruker ikke tid på å lage en ny tegning for dette, men handlingsgangen vil være slik: main() kaller pw.println() under utførelsen av sin println()-metode vil pw-objektet gjøre kall (gjerne flere) til bw.write() under utførelsen av sin write()-metode, vil bw igjen sende kall videre til fw.write() fw sin write()-metode vil så skrive tegn til selve filen Poenget med innpakningen er i begge tilfeller at det ytterste laget (BufferedReader, PrintWriter) har de metodene som det er enklest for oss å benytte for å løse oppgaven. Men disse metodene inneholder ikke kode som er i stand til å jobbe direkte på selve fila (i så fall måtte man ha implementert om igjen i BufferedReader og PrintWriter kode som allerede fantes i de klassene som er lenger inne i innpakningen, hvilket ville vært dumt). Disse objektene benytter dermed igjen objekter som har filbehandlingsmetoder på litt lavere nivå. Samme type innpakning kan man forsåvidt si er til stede i bruken av StringTokenizer. Gitt en vanlig String som inneholdt heltall og mellomrom, kunne vi selv ha vært i stand til å lage nye delstrenger av denne som bare inneholdt sifrene, f.eks. med en for-løkke som så på tegn for tegn i strengen, a la følgende pseudokode: Boolean siffer = false; for hvert tegn i strengen { hvis tegnet er et siffer hvis (siffer == true) sett dette tegnet inn bakerst i den delstrengen du allerede holder på med ellers siffer = true begynn en ny delstreng med dette tallet som første siffer ellers siffer = false } Imidlertid er det unødvendig (og dumt, med mindre man har et spesielt behov for å trene på for-løkker m.m.) å skrive denne koden når man langt enklere kan bruke en ferdig klasse StringTokenizer og oppnå det samme med trivielle kall til hasmoretokens() og nexttoken(). På samme måte kunne man selvsagt også på egen hånd ha laget kode som konverterte en delstreng som kun besto av sifre til det tilsvarende heltallet, f.eks. tall = 0; mult = 1; for (posisjon = bakerste tegn; posisjon >= 0; posisjon- -) { tall = tall + sifferet som står i posisjon * mult; mult = mult * 10; } (hvor det riktignok gjenstår et lite detaljproblem mhp hvordan man får gjort om ett enkelt tegn til det tilsvarende tallet). Imidlertid er det igjen dumt å bruke unødvendig tid på dette når det allerede fins en metode i biblioteket som gjør dette for oss, nemlig Integer.parseInt(String s).