Kommunikation

Inhalt

 

 

Dateien

 

Schreiben und Lesen von Dateien

 

Beispiel -Wörter Zählen

  Dateien als Ressourcen
Dateien im Netz

 

Sockets

 

Übungsaufgaben

Dateien

Wir zuerst Dateien auf der lokalen Festplatte finden und bearbeiten. Dazu verwendet man ein File-Objekt. Die Behandlung und Darstellung von Dateinamen ist systemspezifisch. Java versucht daher, Details zu abstrahieren. Die Klasse File bietet Methoden um nach Dateien zu suchen, sowie Dateinamen darauf zu untersuchen, ob die Dateien les- und schreibbar sind, oder ob der Name gar einem Directory gehört.

Im folgenden Beispiel finden wir alle Dateien mit der Endung .txt. Man könnte dazu auch das Interface FilenameFilter verwenden. Es geht aber auch direkt. Erstellen Sie zum Ausprobieren eine Datei file.txt mit dem Inhalt

Erste Zeile
äöüß

und vielleicht irgend eine zweite Datei mit der Endung .txt. Falls Eclipse verwendett wird, sollte die Datei im Projekt auf oberster Stufe gespeichert werden, da wir das akutelle Verzeichnis lesen. Dieses Verzeichnis ist in Eclipse beim Lauf eines Java-Programms das Verzeichnis des Projekts.

import java.io.File;

public class Test
{
    public static void main (String args[])
    {
        // get the file.txt file
        File file = new File("file.txt");
        if (file.exists() && !file.isDirectory())
        {
            System.out.println("Datei " + file.getName() + " gefunden");
            System.out.println("Voller Name: " + file.getAbsolutePath());
        }

        // get all text files
        File dir = new File(".");
        String[] list = dir.list();
        for (int i = 0; i < list.length; i++)
            if (list[i].endsWith(".txt"))
            {
                long length = new File(dir, list[i]).length(); // get length
                System.out.println(list[i] + " [" + length + "]");
            }

    }
}

Beachten Sie, dass das Paket java.io importiert werden muss. Das Programm gibt den vollen Pfad der Testdatei aus und dann alle Textdateien mit ihrer Länge. Dazu verwendet es die list-Methode von File. Diesmal muss der Dateiname ein Directory sein. Wir verwenden das aktuelle Directory, das gewöhnlich mit einem . bezeichnet wird. Zu jedem gefundenen Namen wird dann ein File-Objekt erzeugt, dessen Methode length die Länge der Datei ergibt.

Datei file.txt gefunden
Voller Name: C:\Users\reneg\OneDrive\Dokumente\Workspaces\Kurs Beispiele\Test\file.txt
file.txt [21]

Schreiben und Lesen von Dateien

Nachdem wir wissen, dass eine Datei test.txt existiert, versuchen wir ihren Inhalt zu lesen.

Dazu verwenden wir einen InputStream, genauer einen FileInputStream. Wir wollen die Datei zunächst zeilenweise lesen. Dazu erzeugen wir einen Stream, der das kann, nämlich den BufferedReader aus unserem FileInputStream. Der Zwischenschritt InputStreamReader wird weiter unten erklärt. Dieser Reader besitzt eine Methode readLine, die null zurückgibt, wenn die Datei zu Ende ist.

import java.io.BufferedReader;
import java.io.FileReader;

public class Test
{
    public static void main (String args[])
    {
        try
        {
            BufferedReader in = new BufferedReader(
                    new FileReader("file.txt"));
            while (true)
            {
                String s = in.readLine();
                if (s == null) break;
                System.out.println(s);
            }
            in.close();
        }
        catch (Exception e)
        {
            System.out.println(e);
        }
    }
}

Erstellt man die Datei unter Windows und lässt das Programm in einer Eingabezeile von Windows laufen, ist die Ausgabe der deutschen Sonderzeichen äöüß möglicherweise nicht korrekt. Dies liegt daran, dass die Eingabezeile die Zeichen anders kodiert als Windows. Wir werden dieses Manko später beheben.

Die Klasse InputStream (und damit auch FileInputStream) besitzt auch die Methode read zum Lesen eines Bytes. Die Methode available liefert die Anzahl der verfügbaren Zeichen.

import java.io.FileInputStream;
import java.io.InputStream;

public class Test
{
    public static void main (String args[])
    {
        try
        {
            InputStream in = new FileInputStream("file.txt");
            while (in.available() > 0)
            {
                System.out.print(in.read() + " ");
            }
            in.close();
        }
        catch (Exception e)
        {
            System.out.println(e);
        }
        System.out.println();
    }
}

Die Ausgabe ist abhängig von der Kodierung (Encoding), mit der die Datei file.txt erstellt wurde. In meinem Fall ist die Datei in UTF-8 erstellt. In diesem fall werden die deutschen Sonderzeichen mit 2 Bytes kodiert, wobei das erste Byte gleich 195 ist.

69 114 115 116 101 32 90 101 105 108 101 13 10 195 164 195 182 195 188 195 159 

69 steht dabei für E, das erste Zeichen in der Datei. Man beachte auch, dass unter Windows das Zeilenende durch 13 und 10 dargestellt wird. Unter Unix wird nur die 10 verwendet, und auf Apple-Rechnern gar nur die 13.

Mit Java stehen Klassen zur Verfügung, die eine Übersetzung der lokalen Codierung in Unicode erlauben. Dies sind die Reader-Klassen. Die Aufgabe wird, wie schon im ersten Beispiel, durch den InputStreamReader erledigt.

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.Reader;

public class Test
{
    public static void main (String args[])
    {
        try
        {
            Reader in = new BufferedReader(
                    new InputStreamReader(
                            new FileInputStream("file.txt"), "UTF-8"));
            while (in.ready())
            {
                int c = in.read();
                if (c == -1) break;
                System.out.print(c + " ");
            }
            in.close();
        }
        catch (Exception e)
        {
            System.out.println(e);
        }
        System.out.println();
        System.out.println(System.getProperty("file.encoding"));
    }
}

Wir geben in desem Programm die Zeichen als Zahl im Unicode-Zeichensatz aus. Java speichert ja auch intern char und String in Unicode. Die Zahl 228 steht für ein 'ä' in Unicode.

69 114 115 116 101 32 90 101 105 108 101 13 10 228 246 252 223 
UTF-8

Beispiel - Zähle Wörter in Bibel

Wir lesen die Wörter in der Englischen Gutenberg-Bibel ein und zählen sie. Dann werden die 10 häufigsten Wörter ausgegeben.

Die Wörter werden in einer Hashtable gespeichert mit dem Zählerstand. Dazu verwenden wir eine eigene Klasse. Der Key für die Hashtable ist einfach das Wort. Man kann durch die Keys einer Hashtable iterieren, indem man die Methode keySet() verwendet, die ein Set aus allen Keys erzeugt.

import java.io.BufferedReader;
import java.io.FileReader;
import java.util.Arrays;
import java.util.Hashtable;

class Count implements Comparable<Count>
{
    String word;
    int count;

    public Count (String word)
    {
        this.word = word;
        count = 1;
    }

    @Override
    public int compareTo (Count o)
    {
        if (count < o.count)
            return -1;
        else if (count > o.count)
            return 1;
        else
            return 0;
    }
}

public class Test
{
    public static void main (String args[])
    {
        try
        {
            // reader from the file
            BufferedReader in = new BufferedReader(new FileReader("bible.txt"));

            // hashtable for word count
            Hashtable<String, Count> wordcount = new Hashtable<String, Count>();

            while (true)
            {
                String line = in.readLine();
                if (line == null) break; // end of file

                // split the line into words
                String words[] = line.split(" ");
                for (String word : words)
                {
                    // "And" is the same as "and"
                    word = word.toLowerCase();     

                    // exclude empty words and section numbers
                    if (word.equals("")) continue;
                    if (word.contains(":")) continue;

                    // put count in hashtable or increase count
                    Count count = wordcount.get(word);
                    if (count == null)
                        wordcount.put(word, new Count(word));
                    else
                        count.count++;
                }
            }

            System.out.print(wordcount.size() + " words found.");

            // Put counts from hashtable to array

            Count[] counts = new Count[wordcount.size()];

            int ci = 0;
            for (String word : wordcount.keySet())
            {
                counts[ci++] = wordcount.get(word);
            }

            // sort array
            Arrays.sort(counts);

            // print 10 most frequent words
            for (int i = 1; i <= 10; i++)
            {
                Count c = counts[counts.length - i];
                System.out.println(c.word + " : " + c.count);
            }

            in.close();
        }
        catch (Exception e)
        {
            System.out.println(e);
        }
    }
}
24781 words found.the : 64011
and : 51313
of : 34631
to : 13567
that : 12784
in : 12503
he : 10261
shall : 9838
unto : 8987
for : 8810

Dateien als Ressourcen

Ressourcen sind Dateien und Einstellungen, die von einem Programm zu seiner Konfiguration benötigt werden. Dateien können zum Beispiel Texte, Grafiken und Sound enthalten. Einstellungen sind Paare von Schlüsseln und Zeichenketten und werden auch in Dateien abgelegt. Alle diese Dateien können entweder neben den Klassendateien auf der lokalen Festplatte abgelegt, oder mit in das jar-Archiv gepackt werden.

Das wesentliche Merkmal einer Ressource ist, dass sie nicht vom Programm geändert werden kann (read only). Es existieren daher keine Schreibzugriffe auf Ressourcen-Dateien.

Wir demonstrieren das Öffnen einer Textdatei als Ressource. Das folgende Programm zeigt eine Textdatei in einem Dialogfenster an. Dies kann zum Beispiel zur Anzeige von Hilfetexten benutzt werden.

Der entscheidende Teil ist, einen Stream zum Lesen von der Datei zu bekommen. Dazu wird die Methode getResourceAsStream(filename) von Class benutzt. Class ist eine allgemeine Klasse, die Klasseninformationen enthält. In diesem Fall benutzen wir eine Instanz von Class, die sich auf die aktuelle Klasse Help bezieht. Diese Instanz bekommen wir mit getClass(), einer Methode, die jedes Objekt hat. Class benutzt eigentlich seinen ClassLoader, der ja die Klassen von der Platte oder aus dem Archiv laden kann. Dieser ClassLoader kann deswegen auch Ressourcen laden.

Man muss noch beachten, dass Ressourcen-Dateien relativ zum aktuellen Verzeichnis, das als Wurzelverzeichnis "/" angegeben wird, bezeichnet werden.

import java.awt.BorderLayout;
import java.awt.Panel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.InputStreamReader;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

class Help extends JFrame implements ActionListener
{
    JTextArea V;
    JButton Close;

    // display the help from the file named subject
    public Help (String subject)
    {
        super("Help");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // TextArea to display the text:
        V = new JTextArea();
        V.setEditable(false);
        JScrollPane scroller = new JScrollPane(V);

        // Read the file from the resource:
        try
        {
            BufferedReader in = new BufferedReader(new InputStreamReader(
                    getClass().getResourceAsStream("/" + subject)));
            // read line after line:
            while (true)
            {
                String s = in.readLine();
                if (s == null) break;
                V.append(s + "\n");
            }
            in.close();
        }
        catch (Exception e)
        {
            V.setText("Could not find the help file!");
        }

        // Layout:
        setLayout(new BorderLayout());
        add("Center", scroller);

        Panel p = new Panel();
        p.add(Close = new JButton("Close"));
        Close.addActionListener(this);
        add("South", p);

        setSize(800, 600);
        setLocationRelativeTo(null);
        setVisible(true);
    }

    @Override
    public void actionPerformed (ActionEvent e)
    // close dialog
    {
        if (e.getSource() == Close)
        {
            setVisible(false);
            dispose();
        }
    }

}

public class Test
{
    public static void main (String args[])
    {
        new Help("test.txt");
    }

}

Dem Konstruktor von Help wird der Dateiname (z.B. "help.txt") als Parameter übergeben. Zu beachten ist noch, dass als Stream auf die Ressource null zurückgegeben wird, wenn die Ressource nicht existiert.

Dateien im Netz

Eines der Designziele von Java war die Programmierung von Netzanwendungen. Java bringt daher eine Reihe von Klassen zur Kommunikation über TCP/IP mit. Eine davon bringt die Möglichkeit, eine Datei direkt von einem WWW-Server zu lesen. Das folgende Beispiel kopiert eine Datei byteweise vom Internet.

Dateien im Netz werden mit einer URL (Uniform Resource Locator) angesprochen. Man kann auch die URL nach dem Erzeugen analysieren, etwa mit den Methoden getProtocol(), getHost() oder getFile(). Wir lesen hier aber nur Byte für Byte und geben das Ergebnis auf einer Datei aus. Dazu wird FileOutputStream verwendet.

import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URL;

public class Test
{
    public static void main (String args[])
    {
        try
        {
            URL url = new URI("http://java.renegrothmann.de/index.html")
                    .toURL();
            InputStreamReader in = new InputStreamReader(url.openStream());
            int b;
            FileOutputStream out = new FileOutputStream("index.html");
            while (true)
            {
                b = in.read();
                if (b == -1) break;
                out.write(b);
            }
            in.close();
            out.close();
        }
        catch (Exception e)
        {
            System.out.println(e);
        }
    }
}

Sockets

Programs can communicate with each other over the internet. Most external connections are blocked by a firewall, which should be installed. We test the communciation only on one machine using Telnet. Most systems contain a telnet client. In Windows, it might be a special feature which needs to be installed. Open a command line and check if the telnet command works on your system.

The following program opens a so-called socket. This is done in a separate thread to show how a program could use a graphical interface and still have a communication channel open. In the example, the main thread simply ends. Our socket can read and write buffered text. It will stop if it receives a line "quit".

To check the program, start it and open another command line where you enter telnet localhost 6000. 6000 is the port that we have assigned to the socket. You can then enter lines and they will be echoed back. Enter quit to finish.

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class Test implements Runnable
{
    PrintWriter Out;
    BufferedReader In;
    ServerSocket server;

    public static void main (String args[])
    {
        new Test();
    }

    public Test ()
    {
        try // generiere Ein- und Ausgabestreams und einen Thread
        {
            server = new ServerSocket(6000);
            server.setSoTimeout(10000);
            Socket socket = server.accept();

            System.out.println("Server opened.");

            Out = new PrintWriter(
                    new DataOutputStream(socket.getOutputStream()), true);
            In = new BufferedReader(new InputStreamReader(
                    new DataInputStream(socket.getInputStream())));
            new Thread(this).start();
        }
        catch (Exception e)
        {
            System.out.println("No connection for 10 seconds.");
        }
    }

    @Override
    public void run ()
    // der Thread, der eine Verbindung verwaltet.
    {
        Out.println("Enter quit to stop.");

        try
        {
            while (true) // Echo-Schleife
            {
                String s = In.readLine();
                if (s.toLowerCase().equals("quit")) break;
                Out.println("Echo: " + s);
            }
            // Streams immer schließen:
            In.close();
            Out.close();
            server.close();
            System.out.println("Server closed");
        }
        catch (Exception e)
        {
            System.out.println(e);
        }
    }
}

Here is a session.

>telnet localhost 6000

Enter quit to stop.
Hi, my name is Paul.
Echo: Hi, my name is Paul.
Okay, I leave.
Echo: Okay, I leave.
quit


Verbindung zu Host verloren.
>

Übungsaufgaben

Lösungen.

Aufgaben ohne Lösung

  1. Schreiben Sie ein Programm, das eine Binärdatei kopieren kann. Die Datei darf natürlich nicht in Unicode übersetzt werden und auch nicht zeilenweise behandelt werden.
  2. Schreiben Sie ein Programm das alle ä, ö, ü usw. in einer Textdatei durch ihr HTML-Äquivalent &auml; etc. ersetzt. (ß ist &szlig;).

Zurück zum Java-Kurs