



<html>
<head>
  <title>javabog.dk -  - Datastr&oslash;mme og filh&aring;ndtering</title>
  <link rev="stylesheet" type="text/css" href="../typografi.css">
  <meta name="description" content="Lærebog i Java. Af Jacob Nordfalk. Udkommet hos Forlaget Globe">
  <meta name="keywords" content="designmønster, programmering, OOP, objekter, klasser, objektorienteret programmering, Java, JSP, lærebog, UML, IT">
</head>
<body bgcolor="#ffffff">



<a href='http://javabog.dk/'>javabog.dk</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='kapitel14.jsp'>&lt;&lt; forrige</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='indhold.jsp'>indhold</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='kapitel16.jsp'>n&aelig;ste &gt;&gt;</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='kode/'>programeksempler</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='../index_OOP.html'>om bogen</a>

<H1 CLASS="western" STYLE="">15 <a name='afsn15'></a>Datastr&oslash;mme
og filh&aring;ndtering</H1>
<P LANG="da-DK" CLASS="kapiteloversigt-western">Indhold:</P>
<UL>
  <LI VALUE=1><P LANG="da-DK" CLASS="kapiteloversigt-western">At
  forst&aring; datastr&oslash;mme</P>
  <LI><P LANG="da-DK" CLASS="kapiteloversigt-western">At l&aelig;se og
  skrive filer</P>
  <LI><P LANG="da-DK" CLASS="kapiteloversigt-western">At analysere
  tekstfiler og udtr&aelig;kke data</P>
  <LI><P LANG="da-DK" CLASS="kapiteloversigt-western">Overblikket over
  og sammenh&aelig;ngen mellem alle datastr&oslash;m-klasserne</P>
</UL>
<P LANG="da-DK" CLASS="kapiteloversigt-western">Kapitlet foruds&aelig;ttes
i <a href='kapitel16.jsp'>kapitel 16</a>, Netv&aelig;rkskommunikation og 18, Serialisering.</P>
<P LANG="da-DK" CLASS="kapiteloversigt-western">Foruds&aelig;tter
<a href='kapitel14.jsp'>kapitel 14</a>, Undtagelser.</P>


<P LANG="da-DK" CLASS="western" STYLE="">En
fil er et arkiv p&aring; et lagermedium, hvori der er gemt data. P&aring;
lagermediet gemmes en r&aelig;kke 0'er og 1-taller (bit) i grupper &aacute;
8 bit, svarende til en byte (et tal mellem 0 og 255). 
</P>
<P LANG="da-DK" CLASS="western">Data kan v&aelig;re gemt <I>bin&aelig;rt</I>,
s&aring;dan at de kun kan l&aelig;ses af et program eller
styresystemet. Det g&aelig;lder f.eks. en .exe-fil eller et dokument
gemt i et propriet&aelig;rt bin&aelig;rt format som f.eks. Word. De
kan ogs&aring; v&aelig;re gemt som <I>tekst</I> uden formatering. Det
g&aelig;lder f.eks. filer, der ender p&aring; .txt, .html og .java.
Oplysningerne i tekstfiler kan l&aelig;ses med en teksteditor<A CLASS="sdfootnoteanc" NAME="sdfootnote1anc" HREF="#sdfootnote1sym"><SUP>1</SUP></A>.
Det er op til programmet, der l&aelig;ser/skriver i filen at afg&oslash;re,
om indholdet er tekst eller bin&aelig;rt.</P>
<P LANG="da-DK" CLASS="western">I Java behandles filer som
datastr&oslash;mme. En datastr&oslash;m er et objekt, som man enten
henter data fra (l&aelig;ser fra en datakilde, f.eks. en fil) eller<A CLASS="sdfootnoteanc" NAME="sdfootnote2anc" HREF="#sdfootnote2sym"><SUP>2</SUP></A>
skriver data til (et datam&aring;l).</P>
<P LANG="da-DK" CLASS="western">Denne arbejdsm&aring;de g&oslash;r,
at forskellige datakilder og -m&aring;l kan behandles ensartet og at
det er let at udskifte datakilden eller -m&aring;let med noget andet
end en fil, f.eks. en forbindelse til netv&aelig;rket.</P>
<H2 CLASS="western">15.1 <a name='afsn15.1'></a>Skrive til en tekstfil</SPAN></H2>
<P LANG="da-DK" CLASS="western">Klassen FileWriter bruges til at
skrive en fil med tekstdata. I konstrukt&oslash;ren angiver man
filnavnet:</P>
<PRE CLASS="kode-western">    FileWriter fil = new FileWriter(&quot;tekstfil.txt&quot;);</PRE><P LANG="da-DK" CLASS="western">
FileWriter-objektet er en datastr&oslash;m, hvis m&aring;l er filen.
Nu kan man skrive tekstdata til filen med:</P>
<PRE CLASS="kode-western">    fil.write(&quot;Her kommer et tal:\n&quot;);
    fil.write(322+&quot;\n&quot;);</PRE><P LANG="da-DK" CLASS="western">
FileWriter-objektets write()-metode er lidt besv&aelig;rlig at
arbejde med, da den ikke underst&oslash;tter linjeskift (som s&aring;
i stedet m&aring; laves med &quot;\n&quot;, en streng med et
linjeskift).</P>
<P LANG="da-DK" CLASS="western">Det er mere bekvemt at l&aelig;gge
objektet ind i en PrintWriter. Et PrintWriter-objekt har print() og
println()-metoder, som vi er vant til og man skal skrive til den
pr&aelig;cis, som n&aring;r man skriver til System.out:</P>
<PRE CLASS="kode-western">    PrintWriter ud = new PrintWriter(fil);
    ud.println(&quot;Her kommer et tal:&quot;);
    ud.println(322);</PRE><P LANG="da-DK" CLASS="western">
N&aring;r vi skriver til PrintWriter-objektet, sender det teksten
videre til FileWriter-objektet, der skriver teksten i filen.</P>
<P LANG="da-DK" CLASS="western">Det er vigtigt at lukke filen, n&aring;r
man er f&aelig;rdig med at skrive. Ellers kan de sidste data g&aring;
tabt! Det g&oslash;r man ved at lukke datastr&oslash;mmen, man skrev
til:</P>
<PRE CLASS="kode-western" STYLE="text-align: justify">    ud.close();</PRE>
<P LANG="da-DK" CLASS="western" STYLE="">Her
er et samlet eksempel, der skriver nogle fiktive personers navn, k&oslash;n
og alder til en fil:</P>
<IMG SRC="bog16_html_34d4ddef.gif" NAME="Objekt15" ALIGN=RIGHT HSPACE=4>
<PRE CLASS="kode-western">import java.io.*;
public class SkrivTekstfil 
{
  public static void main(String[] arg) throws IOException
  {
<B>    FileWriter fil = new FileWriter(&quot;skrevet fil.txt&quot;);</B>
<B>    PrintWriter ud = new PrintWriter(fil);</B>
    for (int i=0; i&lt;5; i++)
    {
      String navn = &quot;person&quot;+i;
      String k&oslash;n;
      if (Math.random()&gt;0.5) k&oslash;n = &quot;m&quot;; else k&oslash;n = &quot;k&quot;;
      int alder = 10+(int) (Math.random()*60);

<B>      ud.println(navn+&quot; &quot;+k&oslash;n+&quot; &quot;+alder);</B>
    }
<B>    ud.close()</B>; <I>// luk s&aring; alle data skrives til disken</I>
    System.out.println(&quot;Filen er gemt.&quot;);
  }
}</PRE>
<HR>
<PRE CLASS="kode-western">Filen er gemt.</PRE><P LANG="da-DK" CLASS="western">
Eventuelle IO-undtagelser (f.eks. ikke mere plads p&aring; disken)
tager vi os ikke af, men sender dem videre til styresystemet
(main()-metoden er erkl&aelig;ret som &quot;throws IOException&quot;).</P>
<P LANG="da-DK" CLASS="western">Efter at programmet har k&oslash;rt,
findes filen &quot;skrevet fil.txt&quot; p&aring; disken, med
indhold:</P>
<PRE CLASS="kode-western">person0 m 34
person1 m 26
person2 m 24
person3 k 51
person4 k 16</PRE><H2 CLASS="western">
15.2 <a name='afsn15.2'></a>L&aelig;se fra en tekstfil</SPAN></H2>
<P LANG="da-DK" CLASS="western">Lad os l&aelig;se filen ovenfor og
skrive den ud til sk&aelig;rmen. 
</P>
<P LANG="da-DK" CLASS="western">Til det form&aring;l bruger vi et
FileReader-objekt som datakilde. Igen pakker vi det ind i et andet
objekt, denne gang af klassen BufferedReader. BufferedReader g&oslash;r
det mere bekvemt, da indl&aelig;sning kan ske linje for linje med
metoden readLine(). N&aring;r der ikke er flere data, returnerer
readLine() null.</P>
<IMG SRC="bog16_html_m74eaa125.gif" NAME="Objekt16" ALIGN=RIGHT HSPACE=4>
<PRE CLASS="kode-western">import java.io.*;
import java.util.*;

public class LaesTekstfil
{
  public static void main(String[] arg) throws IOException
  {
<B>    FileReader fil = new FileReader(&quot;skrevet fil.txt&quot;);</B>
<B>    BufferedReader ind = new BufferedReader(fil);</B>

    String linje = ind.readLine();
    while (linje != null)
    {
      System.out.println(&quot;L&aelig;st: &quot;+linje);
      linje = <B>ind.readLine()</B>;
    }
  }
}</PRE>
<HR>
<PRE CLASS="kode-western">L&aelig;st: person0 m 34
L&aelig;st: person1 m 26
L&aelig;st: person2 m 24
L&aelig;st: person3 k 51
L&aelig;st: person4 k 16</PRE>
<H2 CLASS="western" STYLE="">15.3 <a name='afsn15.3'></a>Analysering
af tekstdata</SPAN></H2>
<P LANG="da-DK" CLASS="western">Ofte er det ikke nok bare at indl&aelig;se
data, de skal ogs&aring; kunne behandles bagefter. 
</P>
<P LANG="da-DK" CLASS="western">For eksempel kunne det v&aelig;re
sjovt at udregne aldersgennemsnittet i LaesTekstfil.java. Det kr&aelig;ver,
at vi f&oslash;rst opdeler data i bidder, for at finde kolonnen med
aldrene og derefter konverterer dem til tal, der kan regnes p&aring;.
</P>
<H3 CLASS="western">15.3.1 <a name='afsn15.3.1'></a>Opdele strenge og konvertere bidderne til
tal</H3>
<P LANG="da-DK" CLASS="western">String-klassen har metoden split(),
der kan dele en streng op i et array bidder. S&aring;ledes vil</P>
<PRE CLASS="kode-western">  String tekst = &quot;Hej, k&aelig;re venner!&quot;
  String[] bidder = tekst.split(&quot; &quot;);</PRE><P LANG="da-DK" CLASS="western">
opdele teksten efter mellemrum og give tre bidder tekst, hhv. &quot;Hej,&quot;,
&quot;k&aelig;re&quot; og &quot;venner!&quot;.</P>
<P LANG="da-DK" CLASS="western">Argumentet til split() er et <I>regul&aelig;rt
udtryk</I>, hvilket vil sige at man kan lave meget sofistikerede
opdelinger (l&aelig;s mere om regul&aelig;re udtryk i
javadokumentationen til split()).</P>
<P LANG="da-DK" CLASS="western">Integer- og Double-klasserne har
metoderne hhv. parseInt() og parseDouble() til at oms&aelig;tte en
streng til et tal<A CLASS="sdfootnoteanc" NAME="sdfootnote3anc" HREF="#sdfootnote3sym"><SUP>3</SUP></A>.
De f&aring;r en streng som parameter og returnerer den &oslash;nskede
type:</P>
<PRE CLASS="kode-western">  int i = Integer.parseInt(&quot;542&quot;);
  double d = Double.parseDouble(&quot;3.14&quot;);</PRE><P LANG="da-DK" CLASS="western">
Eksponentiel notation (hvor 9.8E3 betyder 9800) og andre
talsystemer end titalsystemet forst&aring;s ogs&aring;. F.eks. giver
Integer.parseInt(&quot;00010011&quot;,2) tallet 19 (som er 00010011 i
det bin&aelig;re talsystem) og Integer.parseInt(&quot;1F&quot;,16)
giver 31 (1F i det hexadecimale talsystem):</P>
<PRE CLASS="kode-western">  d = Double.parseDouble(&quot;9.8E3&quot;);        <I>// d = 9800</I>
  i = Integer.parseInt(&quot;00010011&quot;,2);      <I>// i = 19</I>
  i = Integer.parseInt(&quot;1F&quot;,16);        <I>// i = 31</I></PRE><P LANG="da-DK" CLASS="western">
Her er et samlet eksempel, der l&aelig;ser filen og finder summen af
alle personernes alder:</P>
<PRE CLASS="kode-western">import java.io.*;

public class LaesTekstfil2
{
  public static void main(String[] arg) throws IOException
  {
    BufferedReader ind = new BufferedReader(new FileReader(&quot;skrevet fil.txt&quot;));
    String linje = ind.readLine();
    int alderssum = 0;
    while (linje != null)
    {
      String[] bidder = linje.split(&quot; &quot;);     <I>// opdel i bidder efter mellemrum</I>
      alderssum = alderssum + Integer.parseInt( bidder[2] ); <I>// brug tredje bid</I>
      linje = ind.readLine();
    }
    System.out.println(&quot;Summen af aldrene er: &quot;+alderssum);
  }
}</PRE>
<HR>
<PRE CLASS="kode-western">Summen af aldrene er: 151</PRE>
<H3 CLASS="western">15.3.2 <a name='afsn15.3.2'></a>Indl&aelig;sning af tekst med
Scanner-klassen</H3>
<P LANG="da-DK" CLASS="western">For et tekstbaseret (ikke-grafisk)
program skal uddata som bekendt skrives til System.out. 
</P>
<P LANG="da-DK" CLASS="western">Det modsvarende objekt til at l&aelig;se
fra tastaturet, System.in, er en byte-baseret (bin&aelig;r)
datastr&oslash;m. Det er nemmest at pakke den ind i et
Scanner-objekt, som vist <a href='kapitel2.jsp#afsn2.3.1'>afsnit 2.3.1</a>:</P>
<PRE CLASS="kode-western">import java.util.*;
...
    Scanner tastatur = new Scanner(System.in); 
    int alder = tastatur.nextInt();     // l&aelig;s &eacute;t tal
    String navn = tastatur.next();      // l&aelig;s tekst til f&oslash;rste mellemrum
    String linje = tastatur.nextLine(); // l&aelig;s (resten af) en hel linje</PRE><H4 CLASS="western">
Eksempel: Statistik</H4>
<P LANG="da-DK" CLASS="western">Lad os lave et statistikprogram. Vi
t&aelig;ller antallet af personer (linjer i filen) og summen af
aldrene. Linjerne analyseres og l&aelig;gges ind i variablerne navn,
k&oslash;n og alder. 
</P>
<PRE CLASS="kode-western">import java.io.*;
import java.util.*;
public class LaesTekstfilOgLavStatistik
{
  public static void main(String[] arg)
  {
    int antalPersoner = 0;
    int sumAlder = 0;

    try 
    {
      Scanner sc = new Scanner(new FileReader(&quot;skrevet fil.txt&quot;));

      while (sc.hasNext())
      {
        try 
        {
          String navn = sc.next(); <I>// l&aelig;s tekst til f&oslash;rste mellemrum</I>
          String k&oslash;n = sc.next();  <I>// l&aelig;s tekst til n&aelig;ste mellemrum</I>
          int alder = sc.nextInt();<I>// l&aelig;s &eacute;t tal</I> 

          System.out.println(navn+&quot; er &quot;+alder+&quot; &aring;r.&quot;);
          antalPersoner = antalPersoner + 1;
          sumAlder = sumAlder + alder;
        } 
        catch (Exception u) 
        {
          System.out.println(&quot;Fejl. Linjen springes over.&quot;);
          u.printStackTrace();
        }
        sc.hasNextLine();          <I>// hop til n&aelig;ste linje</I>
      }

      System.out.println(&quot;Aldersgennemsnittet er: &quot;+sumAlder/antalPersoner);
    } 
    catch (FileNotFoundException u)
    {
      System.out.println(&quot;Filen kunne ikke findes.&quot;);
    } 
    catch (Exception u) 
    {
      System.out.println(&quot;Fejl ved l&aelig;sning af fil.&quot;);
      u.printStackTrace();
    }
  }
}</PRE>
<HR>
<PRE CLASS="kode-western">person0 er 34 &aring;r.
person1 er 26 &aring;r.
person2 er 24 &aring;r.
person3 er 51 &aring;r.
person4 er 16 &aring;r.
Aldersgennemsnittet er: 30</PRE><P LANG="da-DK" CLASS="western">
Undervejs kan der opst&aring; forskellige undtagelser. Hvis filen
ikke eksisterer, udskrives &quot;Filen kunne ikke findes&quot; og
programmet afslutter. En anden mulig fejl er, at filen er tom. S&aring;
vil der opst&aring; en aritmetisk undtagelse (division med nul), n&aring;r
vi dividerer med antalPersoner og &quot;Fejl ved l&aelig;sning
af fil&quot; udskrives.</P>
<P LANG="da-DK" CLASS="western">Under analyseringen af linjen, kan
der ogs&aring; opst&aring; flere slags undtagelser: Konverteringen
til heltal kan g&aring; galt og der kan v&aelig;re for f&aring; eller
for mange bidder. Hvis disse fejl opst&aring;r, forts&aelig;tter
programmet efter catch-blokken med, at l&aelig;se n&aelig;ste linje
af inddata. 
</P>
<P LANG="da-DK" CLASS="western">Da sumAlder og antalPersoner &aelig;ndres
sidst i try-blokken, vil de kun blive opdateret, hvis hele
linjen er i orden og statistikken udregnes derfor kun p&aring;
grundlag af de gyldige linjer.</P>

<H2 CLASS="western" STYLE="">15.4 <a name='afsn15.4'></a>Bin&aelig;r
l&aelig;sning og skrivning</SPAN></H2>
<P LANG="da-DK" CLASS="western">Arbejder man med bin&aelig;re data
(f.eks lyd- eller billedfiler), skal det ske bin&aelig;rt. En anden
grund til at arbejde med bin&aelig;re data er, at overs&aelig;ttelsen
fra bin&aelig;re til tekstdata tager tid.</P>
<P LANG="da-DK" CLASS="western">Det f&oslash;lgende eksempel
kopierer en fil (bog.html) bin&aelig;rt til en anden fil:</P>
<PRE CLASS="kode-western">import java.io.*;

public class KopierFil
{
  public static void main(String[] arg) throws IOException
  {
    InputStream <B>is = new FileInputStream(&quot;bog.html&quot;)</B>;
    OutputStream <B>os = new FileOutputStream(&quot;kopieretBog.html&quot;)</B>;

<I>    // brug evt. buffere i l&aelig;sning og skrivning (mere effektivt)        punkt A</I>
<I>    // is = new BufferedInputStream(is);</I>
<I>    // os = new BufferedOutputStream(os);</I>

<I>    // husk starttidpunkt, s&aring; vi kan m&aring;le hvor lang tid det teger</I>
    long starttid = System.currentTimeMillis();

<I>    // l&aelig;s og skriv &eacute;n byte ad gangen (ret ineffektivt)                 punkt B</I>
    int b = is.read();
    while (b != -1)
    {
      os.write(b);
      b = is.read();
    }

    is.close();
    os.close();
    long sluttid = System.currentTimeMillis();
    System.out.println(&quot;Kopiering tog &quot;+ (sluttid-starttid)*0.001 +&quot; sek.&quot;);

  }
}</PRE>
<HR>
<PRE CLASS="kode-western">Kopiering tog 4.713 sek.</PRE><P LANG="da-DK" CLASS="western" STYLE="margin-top: 0cm; margin-bottom: 0cm">
Programmet, som det umiddelbart ser ud (punkt A er kommenteret ud),
udf&oslash;rer opgaven ved at l&aelig;se &eacute;n byte fra filen,
skrive den til den anden fil, l&aelig;se en ny byte o.s.v. Det g&aring;r
ret langsomt, filen bog.html, der fylder ca. 100 kb, tager knap fem
sekunder at kopiere.</P>
<H3 CLASS="western">15.4.1 <a name='afsn15.4.1'></a>Optimering af ydelse</H3>
<P LANG="da-DK" CLASS="western">Her udf&oslash;res programmet igen,
med punkt A kommenteret ind, s&aring; der bruges buffere til l&aelig;sning
og skrivning. Programmet udskriver da:</P>
<PRE CLASS="kode-western">Kopiering tog 0.089 sek.</PRE><P LANG="da-DK" CLASS="western">
Vi opn&aring;r alts&aring; en hastighedsfor&oslash;gelse p&aring;
over en faktor halvtreds (!) ved at bruge buffere. Bufferne tager
h&oslash;jde for vores en-byte-ad-gangen-kopiering og bevirker, at
l&aelig;sning og skrivning sker i klumper &aacute; et par kilobyte,
hvilket er langt mere effektivt.</P>
<P LANG="da-DK" CLASS="western">Vi kunne ogs&aring; selv s&oslash;rge
for, at data bliver l&aelig;st i st&oslash;rre klumper, ved at bruge
et array (beskrevet i <a href='kapitel8.jsp'>kapitel 8</a>). Erstatter vi punkt B med det
f&oslash;lgende, g&aring;r det endnu hurtigere:</P>
<PRE CLASS="kode-western"><I>    // l&aelig;s og skriv i st&oslash;rre klumper (mere effektivt)</I>
    byte[] data = new byte[4096]; <I>// 4 kilobyte</I>
    int lgd = is.read(data);
    while (lgd != -1)
    {
      os.write(data, 0, lgd);
      lgd = is.read(data);
    }</PRE>
<HR>
<PRE CLASS="kode-western">Kopiering tog 0.0060 sek.</PRE><P LANG="da-DK" CLASS="western">
Denne tid (75 gange hurtigere) er uafh&aelig;ngig af, om der bruges
buffere eller ej (i punkt A).</P>
<BLOCKQUOTE LANG="da-DK" CLASS="definition-western">Brug buffere
eller s&oslash;rg for, at dit program behandler data i st&oslash;rre
klumper</BLOCKQUOTE>
<H2 CLASS="western" STYLE="">15.5 <a name='afsn15.5'></a>Appendiks</SPAN></H2>
<P LANG="da-DK" CLASS="western">I pakken java.io findes omkring 40
klasser, der kan l&aelig;se eller skrive bin&aelig;re eller
tegnbaserede data fra et v&aelig;ld af datakilder eller -m&aring;l
og p&aring; et v&aelig;ld af forskellige m&aring;der. Der henvises
til javadokumentationen for en n&aelig;rmere beskrivelse af de
enkelte klasser. 
</P>
<P LANG="da-DK" CLASS="western">N&aelig;sten alle metoderne i
klasserne kan kaste en IOException-undtagelse, som skal fanges i en
try-catch-blok (eller kastes videre som beskrevet i kapitlet om
undtagelser).</P>
<H3 CLASS="western">15.5.1 <a name='afsn15.5.1'></a>Navngivning</H3>
<P LANG="da-DK" CLASS="western">Datastr&oslash;mmene kan ordnes i
fire grupper og den konsistente navngivning g&oslash;r dem lettere at
overskue:</P>
<P LANG="da-DK" CLASS="western"> <B>InputStream</B>-objekter l&aelig;ser
bin&aelig;re data.  <B>OutputStream</B>-objekter skriver bin&aelig;re
data. 
</P>
<P LANG="da-DK" CLASS="western"> <B>Reader</B><SPAN STYLE="font-weight: medium">-objekter
l&aelig;ser tekstdata.         </SPAN><B>Writer</B><SPAN STYLE="font-weight: medium">-objekter
skriver tekstdata.</SPAN></P>
<H3 CLASS="western">15.5.2 <a name='afsn15.5.2'></a>Bin&aelig;re data ( -OutputStream og
-InputStream)</H3>
<P LANG="da-DK" CLASS="western">Byte-baserede data som f.eks.
billeder, lyde eller andre bin&aelig;re programdata h&aring;ndteres
af klasser, der arver fra InputStream eller OutputStream.</P>
<P LANG="da-DK" ALIGN=CENTER STYLE="margin-top: 0.11cm; margin-bottom: 0.11cm">
<IMG SRC="bog16_html_m1c20e5c6.gif" NAME="Objekt29"></P>
<P LANG="da-DK" CLASS="western">Af klassediagrammet ses, at metoderne
i InputStream og OutputStream l&aelig;ser og skriver byte-data:
write(byte[]) p&aring; OutputStream skriver et array (en r&aelig;kke)
af byte. Arvingerne har lignende metoder (disse er ikke vist).</P>
<P LANG="da-DK" CLASS="western">InputStream og OutputStream er tegnet
i kursiv. Det er fordi de er <I>abstrakte</I> klasser og det betyder,
at man ikke kan oprette InputStream og OutputStream-objekter direkte
med f.eks. new InputStream(). I stedet skal man bruge en af
nedarvingerne. Abstrakte klasser bliver behandlet i <a href='kapitel21.jsp'>kapitel 21</a>, Avancerede klasser.</P>
<H3 CLASS="western">15.5.3 <a name='afsn15.5.3'></a>Tekstdata ( -Writer og -Reader)</H3>
<P LANG="da-DK" CLASS="western">Tegn-baserede data bruges til
tekstfiler, til at l&aelig;se brugerinput og til meget
netv&aelig;rkskommunikation. Dette h&aring;ndteres af klasserne,
der nedarver fra Reader og Writer.</P>
<P LANG="da-DK" ALIGN=CENTER STYLE="margin-top: 0.11cm; margin-bottom: 0.11cm">
<IMG SRC="bog16_html_73948624.gif" NAME="Objekt30"></P>
<P LANG="da-DK" CLASS="western">Af klassediagrammet ses, at alle
metoderne i Reader og Writer l&aelig;ser og skriver tegndata
(datatype char). Tegn repr&aelig;senteres i Java som 16-bit
unikode-v&aelig;rdier og man kan derfor arbejde med, ikke blot
det vesteurop&aelig;iske tegns&aelig;t, men ogs&aring; det
&oslash;steurop&aelig;iske, det kinesiske alfabet, det japanske,
det kyrilliske, det gr&aelig;ske o.s.v..</P>
<H3 CLASS="western">15.5.4 <a name='afsn15.5.4'></a>Fill&aelig;sning og -skrivning (File- )</H3>
<P LANG="da-DK" CLASS="western">Klasserne til filh&aring;ndtering er
FileInputStream, FileReader, FileOutputStream og FileWriter.</P>
<H3 CLASS="western">15.5.5 <a name='afsn15.5.5'></a>Strenge (String- )</H3>
<P LANG="da-DK" CLASS="western">Med StringReader kan man l&aelig;se
data fra en streng, som om det kom fra en datastr&oslash;m. Det kan
v&aelig;re praktisk til f.eks. at simulere indtastninger fra
tastaturet under test eller input fra en fil (sml. <a href='kapitel15.jsp#afsn15.3.2'>afsnit 15.3.2</a>, Indl&aelig;sning af tekst med Scanner-klassen).</P>
<PRE CLASS="kode-western">  StringReader tegnl&aelig;ser = new StringReader(&quot;Jacob\n4\n5.14\n&quot;);
  BufferedReader ind = new BufferedReader( tegnl&aelig;ser );</PRE><P LANG="da-DK" CLASS="western">
StringWriter er en datastr&oslash;m, der gemmer data i et
StringBuilder-objekt (se <a href='kapitel3.jsp#afsn3.10.1'>afsnit 3.10.1</a>). N&aring;r man er f&aelig;rdig
med at skrive, kan man f&aring; den samlede streng ud ved at kalde
toString().</P>
<H3 CLASS="western" STYLE="">15.5.6 <a name='afsn15.5.6'></a>Arrays
(ByteArray- og CharArray- )</H3>
<P LANG="da-DK" CLASS="western">Et array er en liste eller r&aelig;kke
af noget (se <a href='kapitel8.jsp'>kapitel 8</a>). Ligesom man kan behandle en streng som
en datastr&oslash;m, kan man ogs&aring; arbejde med et array som
datakilde eller -m&aring;l. Klasserne CharArrayReader og
CharArrayWriter hhv. l&aelig;ser og skriver fra et array af
tegn, mens ByteArrayInputStream og ByteArrayOutputStream l&aelig;ser
og skriver bin&aelig;rt i et array af byte.</P>
<H3 CLASS="western">15.5.7 <a name='afsn15.5.7'></a>L&aelig;se og skrive objekter (Object- )</H3>
<P LANG="da-DK" CLASS="western">Det er muligt at skrive hele objekter
ned i en datastr&oslash;m med ObjectOutputStream. Objekterne bliver
da &quot;serialiseret&quot;, dvs. dets data gemmes i datastr&oslash;mmen.
Refererer objektet til andre objekter, bliver disse ogs&aring;
serialiseret og s&aring; fremdeles. Dette er nyttigt til at gemme en
hel graf af objekter p&aring; disken, for senere at hente den frem
igen. Emnet vil blive behandlet mere i <a href='kapitel18.jsp'>kapitel 18</a>, Serialisering.</P>
<H3 CLASS="western">15.5.8 <a name='afsn15.5.8'></a>Dataopsamling (Buffered- )</H3>
<P LANG="da-DK" CLASS="western">Klasserne BufferedInputStream,
BufferedReader, BufferedOutputStream og BufferedWriter s&oslash;rger
for en buffer (et opsamlingsomr&aring;de) til datastr&oslash;mmen.
Det sikrer mere effektiv indl&aelig;sning, fordi der bliver
l&aelig;st/skrevet st&oslash;rre blokke data ad gangen.</P>
<P LANG="da-DK" CLASS="western">BufferedReader s&oslash;rger ogs&aring;
for, at man kan l&aelig;se en hel linje af datastr&oslash;mmen ad
gangen.</P>
<H3 CLASS="western">15.5.9 <a name='afsn15.5.9'></a>G&aring; fra bin&aelig;re til tegnbaserede
datastr&oslash;mme</H3>
<P LANG="da-DK" CLASS="western">Nogen gange st&aring;r man med en
bin&aelig;r datastr&oslash;m og &oslash;nsker at arbejde med den, som
om den var tekstbaseret. Der er to klasser, der konverterer til
tegnbaseret indl&aelig;sning og -udl&aelig;sning:</P>
<P LANG="da-DK" CLASS="western"><B>InputStreamReader</B> er et
Reader-objekt, der l&aelig;ser fra en InputStream (byte til tegn).</P>
<P LANG="da-DK" CLASS="western"><B>OutputStreamWriter</B> er et
Writer-objekt, der skriver til en OutputStream (tegn til byte).</P>
<H3 CLASS="western">15.5.10 <a name='afsn15.5.10'></a>Filtreringsklasser til konvertering og
databehandling</H3>
<P LANG="da-DK" CLASS="western">Klasserne, der arver fra
FilterOutputStream og FilterInputStream, s&oslash;rger alle for en
eller anden form for behandling og pr&aelig;sentation, der letter
programm&oslash;rens arbejde:</P>
<P LANG="da-DK" CLASS="western"><B>LineNumber</B>-klasser t&aelig;ller
antallet af linjeskift i datastr&oslash;mmen, men lader den ellers
v&aelig;re u&aelig;ndret.</P>
<P LANG="da-DK" CLASS="western"><B>Pushback</B>-klasser giver
mulighed for at skubbe data tilbage i datastr&oslash;mmen (nyttigt,
hvis man af en eller anden grund kan &quot;komme til&quot; at l&aelig;se
for langt).</P>
<P LANG="da-DK" CLASS="western"><B>SequenceInputStream</B> s&aelig;tter
to eller flere datakilder i forl&aelig;ngelse af hinanden.</P>
<P LANG="da-DK" CLASS="western"><B>Piped</B>-klasserne letter
datakommunikationen mellem to tr&aring;de (samtidige
programudf&oslash;relsespunkter i et program) ved at s&aelig;tte
data &quot;i k&oslash;&quot; s&aring;dan, at en tr&aring;d kan l&aelig;se
fra datastr&oslash;mmen og en anden skrive.</P>
<P LANG="da-DK" CLASS="western"><B>Checked</B>-klasserne (i pakken
java.util.zip) udregner en checksum p&aring; data. Det kan v&aelig;re
nyttigt til at unders&oslash;ge, om nogen eller noget har &aelig;ndret
data (f.eks. en cracker eller en d&aring;rlig diskette). Man skal
angive et checksum-objekt, f.eks. Adler32 eller CRC32.</P>
<P LANG="da-DK" CLASS="western"><B>Zip</B>-klasserne (i
java.util.zip) l&aelig;ser og skriver zip-filer (lavet af f.eks.
WinZip). De er lidt indviklede at bruge, da de er indrettet til at
h&aring;ndtere pakning af flere filer.</P>
<P LANG="da-DK" CLASS="western" STYLE=""><B>GZIP</B>-klasserne
(i java.util.zip) komprimerer og dekomprimerer data med
Lempel-Ziv-kompression, kendt fra filer, der ender p&aring; .gz p&aring;
UNIX-systemer (is&aelig;r Linux). Er nemmere at bruge end
Zip-klasserne, hvis man kun &oslash;nsker at pakke &eacute;n fil.</P>
<H3 CLASS="western">15.5.11 <a name='afsn15.5.11'></a>Brug af p&aring; filtreringsklasser</H3>
<P LANG="da-DK" CLASS="western">Filtreringsklasser skydes ind som
ekstra &quot;indpakning&quot; mellem de andre datastr&oslash;mme.
F.eks. kan</P>
<PRE CLASS="kode-western">PrintWriter ud = new PrintWriter( new FileOutputStream(&quot;fil.txt&quot;));</PRE><P LANG="da-DK" CLASS="western">
&aelig;ndres til ogs&aring; at komprimere uddata, simpelthen ved at
skyde et GZIPOutputStream-objekt ind:</P>
<PRE CLASS="kode-western">PrintWriter ud = new PrintWriter( new GZIPOutputStream( 
                                  new FileOutputStream(&quot;fil.txt.gz&quot;));</PRE>
<P LANG="da-DK" CLASS="western">Herunder l&aelig;ser vi en fil og
udregner filens checksum og antallet af linjer i filen. 
</P>
<PRE CLASS="kode-western">import java.io.*;
import java.util.zip.*;

public class UndersoegFil
{
  public static void main(String[] arg) throws IOException
  {
    FileInputStream <B>fil = new FileInputStream(&quot;skrevet fil.txt&quot;)</B>;
    BufferedInputStream <B>bstr&oslash;m = new BufferedInputStream(fil)</B>;
    CRC32 checksum = new CRC32();
    CheckedInputStream <B>chkstr&oslash;m = new CheckedInputStream(bstr&oslash;m,checksum)</B>;
    InputStreamReader <B>txtstr&oslash;m  = new InputStreamReader(chkstr&oslash;m)</B>;
    LineNumberReader <B>ind        = new LineNumberReader(txtstr&oslash;m)</B>;

    String linje;
    while ((linje=<B>ind.readLine()</B>)!= null) System.out.println(&quot;L&aelig;st: &quot;+linje);

    System.out.println(&quot;Antal linjer: &quot;  +<B>ind.getLineNumber()</B>);
    System.out.println(&quot;Checksum (CRC):&quot; +<B>checksum.getValue()</B>);
  }
}</PRE>
<HR>
<PRE CLASS="kode-western">L&aelig;st: person0 k 43
L&aelig;st: person1 k 10
L&aelig;st: person2 k 16
L&aelig;st: person3 k 11
L&aelig;st: person4 k 21
Antal linjer: 5
Checksum (CRC):3543848051</PRE><P LANG="da-DK" CLASS="western">
L&aelig;g m&aelig;rke til, hvordan vi h&aelig;gter
datastr&oslash;m-objekterne sammen i en k&aelig;de ved hele tiden
at bruge det forrige objekt som parameter til konstrukt&oslash;rerne:
Filindl&aelig;sning, buffering, checksum, g&aring; fra bin&aelig;r
til tekstbaseret indl&aelig;sning (InputStreamReader) og
linjet&aelig;lling.</P>
<P LANG="da-DK" CLASS="western">While-l&oslash;kken er skrevet meget
kompakt med en tildeling (linje=ind.readLine()) og derefter en
sammenligning, om v&aelig;rdien af tildelingen var null ( (..) !=
null).</P>
<H2 CLASS="western" STYLE="">15.6 <a name='afsn15.6'></a>Test dig
selv</SPAN></H2>

	    Dette afsnit er ikke omfattet af Åben Dokumentslicens.<br>
	    Du skal <a href="/index_OOP.html#bestil">købe</a> bogen for at
	    måtte læse dette afsnit.

  <form action="http://javabog.dk/OOP/kapitel15.jsp#afsn15.6">
  <input type='checkbox' name='vis' value='15.6'>Jeg erklærer, at jeg allerede har købt bogen<br />
  <input type='checkbox' name='vis' value='15.6'>Jeg lover at anskaffe den i nær fremtid.<br />
  <input type='submit' value='Vis mig dette afsnit'>
  </form>

	  <H2 CLASS="western">15.7 <a name='afsn15.7'></a>Resum&eacute;</SPAN></H2>

	    Dette afsnit er ikke omfattet af Åben Dokumentslicens.<br>
	    Du skal <a href="/index_OOP.html#bestil">købe</a> bogen for at
	    måtte læse dette afsnit.

  <form action="http://javabog.dk/OOP/kapitel15.jsp#afsn15.7">
  <input type='checkbox' name='vis' value='15.7'>Jeg erklærer, at jeg allerede har købt bogen<br />
  <input type='checkbox' name='vis' value='15.7'>Jeg lover at anskaffe den i nær fremtid.<br />
  <input type='submit' value='Vis mig dette afsnit'>
  </form>

	  <H2 CLASS="western">15.8 <a name='afsn15.8'></a>Opgaver</SPAN></H2>
<P LANG="da-DK" CLASS="western">Pr&oslash;v eksemplerne fra kapitlet
og:</P>
<OL>
  <LI><P LANG="da-DK" CLASS="western">Udvid
  LaesTekstfilOgLavStatistik.java s&aring;dan, at linjer, der starter
  med &quot;#&quot;, er kommentarer, som ignoreres og afpr&oslash;v,
  om programmet virker.</P>
  <LI><P LANG="da-DK" CLASS="western">Lav et program, der l&aelig;ser
  fra en tekstfil, skyld.txt, og udskriver summen af tallene i hver
  linje med navnet foranstillet (f.eks. <TT CLASS="western">Anne:
  450</TT>). Filen kunne f.eks. indeholde:<BR><TT CLASS="western">Anne
  300 150<BR>Peter 18 300 900 -950<BR>Lis 1000 13.5</TT></P>
  <LI><P LANG="da-DK" CLASS="western">Skriv programmet Grep.java, der
  l&aelig;ser en fil og udskriver alle linjer, der indeholder en
  bestemt delstreng (vink: Ret LaesTekstfil.java - en linje unders&oslash;ges
  for en delstreng med substring(...)).</P>
  <LI><P LANG="da-DK" CLASS="western">Skriv programmet Diff.java, der
  sammenligner to tekstfiler linje for linje og udskriver de linjer,
  der er forskellige.</P>
  <LI><P LANG="da-DK" CLASS="western">Ret SkrivTekstfil.java til
  SkrivKomprimeretTekstfil.java, der gemmer data komprimeret med
  GZIPOutputStream (se appendiks).</P>
  <LI><P LANG="da-DK" CLASS="western">Lav den tilsvarende
  LaesKomprimeretTekstfilOgLavStatistik.java.</P>
  <LI><P LANG="da-DK" CLASS="western">K&oslash;r KopierFil p&aring;
  din maskine og se, hvor lang tid det tager (husk at l&aelig;gge en
  fil med navn bog.html p&aring; ca. 100 kb det rette sted, eller ret
  filnavnet i programmet).<BR>Pr&oslash;v derefter, hvor du bruger
  buffere for mere effektiv l&aelig;sning og skrivning.<BR>Pr&oslash;v
  igen, hvor programmet l&aelig;ser og skriver 4 kb ad gangen.<BR>G&oslash;r
  det nu nogen forskel, om du bruger buffere? Hvorfor/hvorfor ikke?</P>
</OL>

<H2 CLASS="western" STYLE="">15.9 <a name='afsn15.9'></a>Avanceret</SPAN></H2>

	    Dette afsnit er ikke omfattet af Åben Dokumentslicens.<br>
	    Du skal <a href="/index_OOP.html#bestil">købe</a> bogen for at
	    måtte læse dette afsnit.

  <form action="http://javabog.dk/OOP/kapitel15.jsp#afsn15.9">
  <input type='checkbox' name='vis' value='15.9'>Jeg erklærer, at jeg allerede har købt bogen<br />
  <input type='checkbox' name='vis' value='15.9'>Jeg lover at anskaffe den i nær fremtid.<br />
  <input type='submit' value='Vis mig dette afsnit'>
  </form>

	  <H3 CLASS="western">15.9.1 <a name='afsn15.9.1'></a>Klassen RandomAccessFile</H3>

	    Dette afsnit er ikke omfattet af Åben Dokumentslicens.<br>
	    Du skal <a href="/index_OOP.html#bestil">købe</a> bogen for at
	    måtte læse dette afsnit.

  <form action="http://javabog.dk/OOP/kapitel15.jsp#afsn15.9.1">
  <input type='checkbox' name='vis' value='15.9.1'>Jeg erklærer, at jeg allerede har købt bogen<br />
  <input type='checkbox' name='vis' value='15.9.1'>Jeg lover at anskaffe den i nær fremtid.<br />
  <input type='submit' value='Vis mig dette afsnit'>
  </form>

	  <H3 CLASS="western">15.9.2 <a name='afsn15.9.2'></a>Filh&aring;ndtering (klassen File)</H3>

	    Dette afsnit er ikke omfattet af Åben Dokumentslicens.<br>
	    Du skal <a href="/index_OOP.html#bestil">købe</a> bogen for at
	    måtte læse dette afsnit.

  <form action="http://javabog.dk/OOP/kapitel15.jsp#afsn15.9.2">
  <input type='checkbox' name='vis' value='15.9.2'>Jeg erklærer, at jeg allerede har købt bogen<br />
  <input type='checkbox' name='vis' value='15.9.2'>Jeg lover at anskaffe den i nær fremtid.<br />
  <input type='submit' value='Vis mig dette afsnit'>
  </form>

	  <H3 CLASS="western">15.9.3 <a name='afsn15.9.3'></a>Platformuafh&aelig;ngige filnavne</H3>

	    Dette afsnit er ikke omfattet af Åben Dokumentslicens.<br>
	    Du skal <a href="/index_OOP.html#bestil">købe</a> bogen for at
	    måtte læse dette afsnit.

  <form action="http://javabog.dk/OOP/kapitel15.jsp#afsn15.9.3">
  <input type='checkbox' name='vis' value='15.9.3'>Jeg erklærer, at jeg allerede har købt bogen<br />
  <input type='checkbox' name='vis' value='15.9.3'>Jeg lover at anskaffe den i nær fremtid.<br />
  <input type='submit' value='Vis mig dette afsnit'>
  </form>

	  
<DIV ID="sdfootnote1">
  <P LANG="da-DK" CLASS="sdfootnote-western"><A CLASS="sdfootnotesym" NAME="sdfootnote1sym" HREF="#sdfootnote1anc">1</A>Teksten
  kan dog stadig v&aelig;re kryptisk og uforst&aring;elig, som f.eks.
  .java-kildetekst er for en udenforst&aring;ende.</P>
</DIV>
<DIV ID="sdfootnote2">
  <P LANG="da-DK" CLASS="sdfootnote-western"><A CLASS="sdfootnotesym" NAME="sdfootnote2sym" HREF="#sdfootnote2anc">2</A>Det
  er dog muligt at &aring;bne en fil for samtidig l&aelig;sning og
  skrivning med klassen RandomAccessFile, beskrevet i
  Javadokumentationen.</P>
</DIV>
<DIV ID="sdfootnote3">
  <P LANG="da-DK" CLASS="sdfootnote-western"><A CLASS="sdfootnotesym" NAME="sdfootnote3sym" HREF="#sdfootnote3anc">3</A>Tilsvarende
  findes Byte.parseByte(), Long.parseLong(), Float.parseFloat() osv. 
  </P>
</DIV>

<a href='http://javabog.dk/'>javabog.dk</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='kapitel14.jsp'>&lt;&lt; forrige</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='indhold.jsp'>indhold</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='kapitel16.jsp'>n&aelig;ste &gt;&gt;</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='kode/'>programeksempler</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='../index_OOP.html'>om bogen</a>
<hr>
<font size=-2>http://javabog.dk/ - <b></b> af Jacob Nordfalk.
<br>
  Licens og kopiering under <a href='http://www.linuxbog.dk/licens.html'>&Aring;ben Dokumentlicens</a> (&Aring;DL)
  hvor intet andet er nævnt (79% af værket).
</font>
<br>
Ønsker du at se de sidste 21% af dette værk (226970 tegn)
skal du købe bogen. Så får du pæne figurer og layout, stikordsregister og en trykt bog med i købet.
<!-- netlæser: Wget/1.10, autoHent: true  -->
     

</body>
</html>
