Programmering 1999 Forelæsning 8, fredag 24. september 1999 Overview Tekstfiler, tegn og brikker StreamTokenizer: indlæsning fra tekstfiler Formateret udskrift Programmering 1999 KVL Side 8-1 Tekstfiler En tekstfil er en tegnfølge, lagret på disk eller diskette. Tekstfiler hedder typisk A:\addrlist.txt, C:\java\numbers.txt og lignende. Tekstfiler kan bruges til at lagre tekster (ord) såvel som tal. Regneark (Excel), statistikpakker (SAS), og tekstbehandlingsprogrammer kan læse og skrive tekstfiler. Det er nyttig at kunne læse og skrive tekstfiler fra Java programmer også. (Java appletter kan normalt ikke læse og skrive tekstfiler på den lokale disk eller diskette). Programmering 1999 KVL Side 8-2 En tekstfil er en sekvens af tegn Tekstfilen Ole 312.2 Ib 11117 består af 19 tegn: O l e 3 1 2. 2 \n I b 1 1 1 1 7 \n Programmering 1999 KVL Side 8-3 Brikker (tokens) Når vi skal læse tekstfiler er vi normalt interesserede i tal og ord, ikke i de enkelte tegn. Normalt er vi ligeglade med mængden af blanktekst: blanktegn, linieskift og tabulatortegn. Tal og ord kaldes brikker, på engelsk tokens. Brikker adskilles normalt af blanktekst. De 19 tegn i filen udgør 4 brikker: Ole 312.2 Ib 11117 TT_EOF En særlig brik TT_EOF betyder filslut (end of file). Programmering 1999 KVL Side 8-4
Eksempel: Læsning og addition af nogle tal En tekstfil "numbers.txt": 14.5 20 18 19.1 Filen indeholder 4 brikker (samt den særlige filslutbrik TT_EOF): 14.5 20 18 19.1 TT_EOF Vi vil læse alle tallene fra filen "numbers.txt" og lægge dem sammen. Programmering 1999 KVL Side 8-5 Hvordan samle tegn til brikker: StreamTokenizer Den indbyggede klasse StreamTokenizer gør det nemt at læse tal fra en tekstfil. Sådan åbnes filen numbers.txt og omdannes til en strøm tstream af brikker: Reader inp = new FileReader("numbers.txt"); StreamTokenizer tstream = new StreamTokenizer(inp); Brikstrømmen tstream er et objekt af klasse StreamTokenizer. Vi ønsker at læse tal som tal, ikke som ord: tstream.parsenumbers(); Metoder og felter i StreamTokenizer tstream.nexttoken() læser den næste brik (til at begynde med er næste brik blot første brik). tstream.ttype er typen af den aktuelle brik. tstream.nval er talværdien af den aktuelle brik (som double). tstream.sval er strengværdien af den aktuelle brik (som String). Brug StreamTokenizer snarere end StringTokenizer (der bruges i lærebogen). Programmering 1999 KVL Side 8-6 Hvordan læse og addere tallene Læs filen, en brik ad gangen, indtil aktuelle brik er TT_EOF. For hver talbrik der læses, forøg sum med dette tal: Reader inp = new FileReader("numbers.txt"); StreamTokenizer tstream = new StreamTokenizer(inp); tstream.parsenumbers(); double sum = 0; tstream.nexttoken(); while (tstream.ttype!= StreamTokenizer.TT_EOF) sum += tstream.nval; tstream.nexttoken(); System.out.println(sum); Programmering 1999 KVL Side 8-7 Hele historien import java.io.*; public class Filesum public static void main(string[] args) throws IOException Reader inp = new FileReader("numbers.txt"); // 1 double sum = 0; // 4 tstream.nexttoken(); // 5 while (tstream.ttype!= StreamTokenizer.TT_EOF) // 6 sum += tstream.nval; // 7 tstream.nexttoken(); // 8 System.out.println(sum); Erklæringen import java.io.*; skal angives foran klasser der læser fra filer. Erklæringen throws IOException skal angives i metoder der læser fra filer. Det fortæller at metoden kan forårsage en undtagelse (eng. exception ), f.eks. hvis filen ikke findes. Programmering 1999 KVL Side 8-8
Indlæsning linie for linie Antag vi ønsker at finde summen for hver linie. Så skal linieskift ikke behandles som blanktekst, men som en særlig brik. I såfald svarer tekstfilen 14.5 20 18 19.1 til disse brikker 14.5 20 18 TT_EOL 19.1 TT_EOL TT_EOF Programmering 1999 KVL Side 8-9 Indlæsning linie for linie i Java Reader inp = new FileReader("numbers.txt"); // 1 tstream.eolissignificant(true); // A tstream.nexttoken(); // B while (tstream.ttype!= StreamTokenizer.TT_EOF) // C double sum = 0; // D while (tstream.ttype!= StreamTokenizer.TT_EOL) // E sum += tstream.nval; // F tstream.nexttoken(); // G System.out.println(sum); // H tstream.nexttoken(); // I Den ydre while-løkke gennemløbes én gang for hver linie. Den indre while-løkke gennemløbes én gang for hver tal på linien. tstream.eolissignificant(true) siger at linieskift skal blive til brikken TT_EOL Programmering 1999 KVL Side 8-10 Hele historien import java.io.*; public class Linesum public static void main(string[] args) throws IOException Reader inp = new FileReader("numbers.txt"); // 1 tstream.eolissignificant(true); // A tstream.nexttoken(); // B while (tstream.ttype!= StreamTokenizer.TT_EOF) // C double sum = 0; // D while (tstream.ttype!= StreamTokenizer.TT_EOL) // E sum += tstream.nval; // F tstream.nexttoken(); // G System.out.println(sum); // H tstream.nexttoken(); // I Programmering 1999 KVL Side 8-11 T yper af brikker En StreamTokenizer finder automatisk typen for hver brik. Der er fire slags brikker: TT_NUMBER tal TT_WORD ord TT_EOL linieskift (end of line) TT_EOF filslut (end of file) Man kan undersøge om den aktuelle brik er et tal med if (tstream.ttype == TT_NUMBER)... I såfald er tstream.nval brikkens talværdi (som double). Man kan undersøge om den aktuelle brik er et ord med if (tstream.ttype == TT_WORD)... I såfald er tstream.sval brikkens værdi (som String). Programmering 1999 KVL Side 8-12
Formateret udskrift Rå udskrift fra et program kan se ret rodet ud. For eksempel, en liste af byer og deres beregnede gennemsnitstemperaturer: Odense 17.5 Assens 19.1 Slagelse 19.775000000000002 Longyearbyen 8.7 Der er to problemer: Tallene udskrives med varierende (og urealistisk) nøjagtighed. Brug klassen DecimalFormat til at styre antallet af cifre efter decimalsymbolet. Dataene er ikke opstillet i søjler. Det nedsætter læseligheden. Brug metoder til at udvide strenge med blanke til venstre eller højre. Programmering 1999 KVL Side 8-13 Formatering af tal med DecimalFormat For at udskrive d med to cifre efter decimalsymbolet: DecimalFormat fmt = new DecimalFormat("0.00"); System.out.println(fmt.format(3.1415926)); Dette udskriver strengen 3.14. Konstruktoren DecimalFormat opretter et formateringsobjekt ud fra et formatmønster. Formateringsobjektet kan derefter bruges til at formatere tal. Betydningen af formatmønstre: Tegn Betydning # vilkårlig mange cifre; nuller blanke 0 mindst ét ciffer; nuller vises. decimalsymbol (punktum eller komma), tusind-adskiller (ciffergruppering) Programmering 1999 KVL Side 8-14 Nogle typiske formatmønstre (US English) Format patterns Number # #.# #.## 0.0 0.0# 0.00 000.0 #,##0.00 0.0 0 0.0 0.0 0.00 000.0 0.00 0.1 0.1.1 0.1 0.1 0.10 000.1 0.10 1.0 1 1 1 1.0 1.0 1.00 001.0 1.00 1.1 1 1.1 1.1 1.1 1.1 1.10 001.1 1.10-1.1-1 -1.1-1.1-1.1-1.1-1.10-001.1-1.10 330.8 331 330.8 330.8 330.8 330.8 330.80 330.8 330.80 1234.516 1235 1234.5 1234.52 1234.5 1234.52 1234.52 1234.5 1,234.52 Programmering 1999 KVL Side 8-15 Eksempel på formatering af valuta Et formateringsobjekt kan bruges mange gange. import java.text.*; public class Format2 public static void main(string[] args) DecimalFormat fmt = new DecimalFormat("#,##0.00"); double[] arr = 0, 0.1, 1.0, 1.1, -1.1, 330.8, 1234.516 ; for (int i=0; i < arr.length; i++) System.out.println(fmt.format(arr[i])); Dette program udskriver den sidste søjle i tabellen ovenfor. Erklæringen import java.text.*; skal med når man bruger DecimalFormat. Programmering 1999 KVL Side 8-16
Tvungne talformater I Danmark bruges komma, som decimalsymbol og punktum. som tusind-adskiller. Britiske og amerikanske konventioner gør det modsatte. Brug dansk til at publicere på dansk, og til at skrive tekstfiler som skal kunne læses af f.eks. Excel (i Danmark). Man kan styre DecimalFormat i detaljer på denne måde: import java.text.*; public class Format3 public static void main(string[] args) DecimalFormatSymbols decsyms = new DecimalFormatSymbols(); decsyms.setdecimalseparator(, ); decsyms.setgroupingseparator(. ); DecimalFormat fmt = new DecimalFormat("#,##0.00", decsyms); double[] arr = 0, 0.1, 1.0, 1.1, -1.1, 330.8, 1234.516 ; for (int i=0; i < arr.length; i++) System.out.println(fmt.format(arr[i])); Programmering 1999 KVL Side 8-17 Det giver 0,00 0,10 1,00 1,10-1,10 330,80 1.234,52 Programmering 1999 KVL Side 8-18 Opstilling i søjler: udvidelse af strenge med blanke For at (højre)stille tal i søjler skal man udvide dem med blanke til venstre. (Den slags formatering sker automatisk i Excel og SAS.) Denne metode bruger en StringBuffer til gøre strengen s lang nok ved at tilføje blanke: public static String padleft(string s, int width) int filler = width - s.length(); if (filler > 0) // and therefore width > 0 StringBuffer res = new StringBuffer(width); for (int i=0; i<filler; i++) res.append( ); return res.append(s).tostring(); else return s; StringBufferen oprettes med plads til width tegn af hensyn til effektiviteten. Programmering 1999 KVL Side 8-19 Udfyldning af strenge til højre Søjlerne længere til højre kan kun opstilles pænt hvis teksten til venstre fylder lige meget på hver linie. For at udskrive tekst (venstrestillet) så den fylder et bestemt antal pladser, udvides den til højre: public static String padright(string s, int width) int filler = width - s.length(); if (filler > 0) // and therefore width > 0 StringBuffer res = new StringBuffer(width).insert(0, s); for (int i=0; i<filler; i++) res.append( ); return res.tostring(); else return s; Programmering 1999 KVL Side 8-20
Et fuldstændigt eksempel på læsning og formatering Læs en fil places.txt med byer og temperaturobservationer i dette format: Odense 14.5 20 18 Assens 19.1 Slagelse 23.1 25.1 12.1 18.8 Longyearbyen 8.1 10.2 7.8... Beregn og udskriv gennemsnittet af tallene for hver by. Programmering 1999 KVL Side 8-21 import java.io.*; import java.text.decimalformat; public class ReadAndWrite public static void main(string[] args) throws FileNotFoundException, IOException // 0 Reader inp = new FileReader("places.txt"); // 1 tstream.eolissignificant(true); // 4 tstream.nexttoken(); // 5 while (tstream.ttype!= StreamTokenizer.TT_EOF) // 6 double sum = 0; // 7 int count = 0; // 8 String name = tstream.sval; // 9 tstream.nexttoken(); // 10 while (tstream.ttype!= StreamTokenizer.TT_EOL) // 11 sum += tstream.nval; // 12 count++; // 13 tstream.nexttoken(); // 14 double avg = sum / count; // 15 DecimalFormat fmt = new DecimalFormat("0.00"); // 16 System.out.println(padRight(name, 30) // 17 + padleft(fmt.format(avg), 10)); // 18 tstream.nexttoken(); // 19... Programmering 1999 KVL Side 8-22 Uddata er Odense 17.50 Assens 19.10 Slagelse 19.78 Longyearbyen 8.70... Programmering 1999 KVL Side 8-23 Læs noterne Text files in Java Husk Brug StreamTokenizer til at læse data fra tekstfiler StreamTokenizer laver en strøm af tegn til en strøm af brikker (tokens) Brug DecimalFormat til at formatere tal med en given præcision (når den en gang kommer til at virke ordentligt) Brug metoderne padleft og padright til simpel opstilling i søjler Programmering 1999 KVL Side 8-24