Input/Output Java IO Writer - Subklassen

nobody76

Mitglied
Hallo, ich habe ein kleines Problem, ich soll eine Decorator Klasse schreiben, welche von der abstrakten Klasse java.io.Writer erbt. Aufgabenstellung:
The writing decorator may, for example, transform all characters to upper case, implement some statistics (e.g. character counts), or implement some cipher algorithm (e.g. ROT13).

Also meine leere Klasse sieht mal so aus:
Java:
import java.io.*;

public class UpperCWriter extends Writer {

    @Override
    public void write(char[] cbuf, int off, int len) throws IOException {
       
    }

    @Override
    public void flush() throws IOException {
       
    }

    @Override
    public void close() throws IOException {
   
    }
}

Ich hab mir gedacht, ich lehne mich an die Klasse BufferedWriter. Nur ich versteh nicht so ganz wie ich das am besten angehe, ich verstehe nicht was flush() machen soll, ich versteh auch nicht wirklich (und jetzt bitte keine Referenzen auf die JavaDoc) was der BufferedWriter macht oder der OutputStreamWriter, im Endeffekt sind das ja alles Subklassen von Writer. Ich kann mir noch nicht so richtig vorstellen, was ich machen soll. Vielleicht habt ihr ein paar Ansätze, Ideen, Tipps für mich.

LG
 
Hast du dich denn mal mit dem Dekorator Pattern beschäftigt?

Es geht darum eine eine Klasse um eine gewisse Funktionalität zu erweitern bzw. auszuschmücken.
Also musst du deiner Klasse auch eine Klasse übergeben die sie eben erweitern soll.

Also ein erster Ansatz wäre die zu dekorierende Klasse im Konstruktor zu übergeben:
Java:
Writer decoratedWriter;

public UpperCWriter(Writer w) {
    decoratedWriter = w;
}

In der write Methode lässt du den übergebenen Writer dann wieder schreiben, nur eben nicht das originale Array, sondern das Array umgeformt in Großbuchstaben.

Mal als Beispiel:
Java:
@Override
public void write(char[] cbuf, int off, int len) throws IOException {
    for (int i = 0; i < len; i++) {
        decoratedWriter.write(Character.toUpperCase(cbuf[i + off]));
    }
}

@Override
public void flush() throws IOException {
    decoratedWriter.flush();
}

public static void main(String[] args) throws IOException {
    UpperCWriter writer = new UpperCWriter(new PrintWriter(System.out));
    char[] msg = "lower case testnachricht".toCharArray();
    writer.write(msg, 0, msg.length);
    writer.flush();
}
 
Hey vielen Dank für deine Antwort!!!
Also ich habe mir das Pattern angeschaut anhand dieses Beispiels:
einleitung6.svg

Also der Decorator wäre ja die Beilage oder? Also in unserem Fall der Writer und BufferedWriter, FileWriter, etc. wären die Klassen, die von der Beilage erben.

Aber ich glaub ich verstehe es noch nicht ganz wie es funktioniert, zumindest nicht in der java.io Hierarchie.
Es geht darum eine eine Klasse um eine gewisse Funktionalität zu erweitern bzw. auszuschmücken.
Also musst du deiner Klasse auch eine Klasse übergeben die sie eben erweitern soll.
mit deiner Klasse meinst du jetzt die UpperCWriter Klasse oder? Wie kann ich das verstehen, dass ich ihr eine Klasse übergeben muss, die sie erweitern soll?

Und was kann dieses flush() ???

LG
 
mit deiner Klasse meinst du jetzt die UpperCWriter Klasse oder? Wie kann ich das verstehen, dass ich ihr eine Klasse übergeben muss, die sie erweitern soll?
Mein Codebeispiel deutet das eig. an. Im Konstruktor übergibst du eine Instanz vom Typ Writer. Der übergebene Writer wird dann mit den Eigenschaften des UpperCWriters erweitert.

Und was kann dieses flush() ???
LG
Bei sowas hilft es immer, wenn man einfach in die Dokumentation guckt:
public abstract void flush()
throws IOException
Flushes the stream. If the stream has saved any characters from the various write() methods in a buffer, write them immediately to their intended destination. Then, if that destination is another character or byte stream, flush it. Thus one flush() invocation will flush all the buffers in a chain of Writers and OutputStreams.
If the intended destination of this stream is an abstraction provided by the underlying operating system, for example a file, then flushing the stream guarantees only that bytes previously written to the stream are passed to the operating system for writing; it does not guarantee that they are actually written to a physical device such as a disk drive.

Specified by:
flush in interface Flushable
Throws:
IOException - If an I/O error occurs
 
Okay vielen Dank.
Wenn ich das ganze jetzt umgekehrt machen will, also einen Decorator für die abstrakte Reader Klasse, zB ich lese ein File mit lauter Großbuchstaben und will dass es mein Reader übersetzt in Kleinbuchstaben, wie muss ich da vorgehen??

Java:
import java.io.IOException;
import java.io.Reader;

public class UCReader extends Reader {
  
    private Reader in;

    public UCReader(Reader in){
        this.in = in;
    }

    @Override
    public int read(char[] cbuf, int off, int len) throws IOException {
        //Das hier bringt nichts, len ist immer 1 WARUM?
        for(int i = 0; i < len; i++){
            cbuf[i] = 'k';
            System.err.println(len);
        }
        return in.read(cbuf, off, len);
    }

    @Override
    public void close() throws IOException {
        in.close();      
    }

   public static void main(String[] args) throws IOException {
  
     Reader fr = new UCReader(new FileReader("justuppercaseletters.txt"));
    
     StringBuffer sb = new StringBuffer();
     int ch;
     while( (ch=fr.read()) != -1 )
        sb.append((char)ch);

     System.out.println(sb.toString());
   }
}
 
Zuletzt bearbeitet von einem Moderator:
Nein das hilft mir gar nicht, es geht nicht darum wie ich es in LowerCase umschreibe, sondern wie ich das als Decorator der Reader Komponente implementiere. (Die for-Schleife ist nur ein Test-Code, der aber nichts ändert [da ich vermutlich an einer anderen Stelle was ändern muss]).
 
Na das funktioniert so leider nicht, also meinst du in der überschriebenen read Methode? Weil dann müsste ja jetzt (mit dieser for-Schleife) lauter k's drinnen stehen. Tuts aber komischerweise nicht, deshalb denk ich ich muss es irgendwie und irgendwo anders implementieren, nur weiß ich leider nicht genau wo.
 
Na das funktioniert so leider nicht, also meinst du in der überschriebenen read Methode? Weil dann müsste ja jetzt (mit dieser for-Schleife) lauter k's drinnen stehen. Tuts aber komischerweise nicht, deshalb denk ich ich muss es irgendwie und irgendwo anders implementieren, nur weiß ich leider nicht genau wo.
Du solltest auch einfach mal schauen welchen Code du aufrufst 😉

Java:
   public static void main(String[] args) throws IOException {

     Reader fr = new UCReader(new FileReader("justuppercaseletters.txt"));
  
     StringBuffer sb = new StringBuffer();
     int ch;
     while( (ch=fr.read()) != -1 )
        sb.append((char)ch);

     System.out.println(sb.toString());
   }
Du erstellst eine Instanz der Klasse "UCReader" und rufst von dieser Instanz die Methode "read()" auf.
Überschrieben hast du aber "read(char[], int, int)".
 
Danke für deine Antwort. Hmm ja, das ist wohl ein Argument 🙄
Ich würd aber auch gern einen BufferedReader drum rum erstellen zB:
Java:
BufferedReader br = new BufferedReader(new UCReader(new FileReader("test.txt")));

        String zeile1 = br.readLine();
        System.out.println(zeile1);

Dann passiert mit dieser for-Schleife auch wieder nichts, also ich weiß nicht ganz wo ich in read() dieses char-Array herbekommen kann, damit ich es auch da überschreiben kann. Verstehe nicht ganz wie das miteinander in Bezug steht.
 
Java:
    @Override
   public int read(char[] cbuf, int off, int len)throwsIOException{
       //Das hier bringt nichts, len ist immer 1 WARUM?
       for(int i =0; i < len; i++){
            cbuf[i]='k';
            System.err.println(len);
       }
       return in.read(cbuf, off, len);
   }
Also nehmen wir mal an deine read Funktion wird irgendwo ausgeführt.
Dann füllst du das Array mit 'k's, okay. Aber dann im return rufst du die read-Funktion deines FileReaders auf und die überschreibt dir dein Array doch einfach wieder.

Und die readLine Methode vom BufferedReader ist wieder ne ganz andere Geschichte aber okay.
 
Also ich hab gedacht ich kann dann dieses neue cbuf Array dem FileReader übergeben, ah ok das funktioniert so wohl nicht, aber was soll ich den returnen? Das mit dem int verwirrt mich ein bisschen, was passiert den mit dem char[]? Also ich weiß nur, dass es bei -1 fertig ist.
 
Tut mir leid, ich komm nicht mit.
Es wird ja von außen (der Main, oder vom BufferedReader [falls er darauf zugreift], oder von sonst was...) immer auf die read() zugegriffen oder? Aber die abstrakte Klasse Reader verlangt eine Implementierung von read(char[], int, int). Wie kann ich aber in meiner UCReader Klasse, auf das Array (das vom FileReader), also von in, zugreifen, so dass ich es ändern kann, zB jeden Buchstaben durch ein k auswechseln.
Sorry dass ich mich so dumm anstelle.
LG
 
Java:
    @Override
    public int read(char[] cbuf, int off, int len) throws IOException {
        //Erstelle Array in das der FileReader Daten liest
        char[] buf = new char[128];
        int bytesRead = in.read(buf, 0, buf.length);

        //mache irgendwas mit dem array
        Arrays.fill(buf, 0, bytesRead, 'k');
        return bytesRead;
    }

Wobei das read vom FileReader natürlich keinen Sinn macht wenn du es danach sowieso überschreibst mit 'k's.
 
Vielen Dank, aber das cbuf kommt nie mehr vor darin?
Also ich würde eigentlich gerne das "vorhandene" Array (also welches der FileWriter einliest), welches das auch immer ist, überschreiben (bzw. toUpperCase machen) oder so.
 
Das Array in das der FileReader die Daten einliest übergibst du selbst als ersten Paramter (siehe mein Kommentar im Code). Damit kannste dann machen was du willst.
 
Aha okay, also buf ist dann das übergebene Array vom FileReader, okay, und wie könnte ich das jetzt noch so umschreiben, dass es auch zB so greift:
Java:
 BufferedReader br = new BufferedReader(new UPCReader(new FileReader("test.txt")));

        String zeile1 = br.readLine();
        System.out.println(zeile1);
       
        br.close();

Also das wäre ja eigentlich der Sinn vom Decorator oder?
Ich denke mal ich muss auch die read() verändern, so dass diese vielleicht read(char[], int, int) aufruft?
 

Zurück
Oben