Input/Output Auf effiziente Weise einen Stream auszulesen

jf

Bekanntes Mitglied
Hallo, ich bin gerade wieder an Java dran und stehe damit auch wieder vor einigen Problemen... - Ich hoffe ihr könnt mir helfen. 🙂

Ich lade einen Datei von einem FTP-Server (direkt als Sream, um eine temporäre Datei zu vermeiden):
Java:
InputStream inStream = ftp.retrieveFileStream(ftpLogin.ftp_datafile);

Zur weiteren Verarbeitung beöntige ich den Datei-Inhalt als String, hierfür gibt es anscheinend nur die read()-Methoden. Wenn ich mir jedes Zeichen einzeln aus dem Stream hole, um dieses an einen String anzuhängen, dann benötigt die Ausführung allerdings unverhältnismäßig viel Zeit (sehr viel länger, als den Stream in eine Datei zu schreiben!). Ich verwende dabei folgenden Quelltext:

Java:
if(inStream != null) {
	int c;
	while( (c = inStream.read()) != -1 ){
		System.out.println((char)c);		// nur fürs Debugging - es dauert aber auch ohne Konsolenausgabe extrem lang
		fileAsString += Character.valueOf((char)c);
	}
	inStream.close();
}

PS: Ich könnte zwar einen StringBuffer verwenden, aber hier im Forum wurde mir einmal gesagt, dass der Kompilierer bei "String +=" intern sowies eine StringBuffer-Operation anwendet. - Ist dem doch nicht so?

Die überladenen Methoden read(byte[] b) und read(byte[] b, int off, int len) kann ich leider nicht anwenden (zumindest weiß ich nicht wie...), weil ich die Länge der Datei nicht kenne. Ich habe in der FTPClient-Klasse auch keine Methode gefunden, welche mir diese Information liefert. - Oder ist dies evtl. über BufferSize() realisierbar? Bei php gäbe es dafür ja die Funktion ftp_size($id, $filename).

Hat evtl. jemand einen Tipp für mich?
 
Du könntest einmal versuchen, nicht direkt aus dem InputStream zu lesen, sondern diesen mittels eines BufferedInputStreams zu lesen. Der BufferedInputStream liest mehrere Bytes auf einmal (je nach Größe des Buffers) und sollte deswegen um einiges schneller sein.
 
oder doch mit byte[] lesen, das muss nicht genau die passende Größe haben,
wenn zu klein, wovon immer auszugehen ist, dann sowieso in einer Schleife lesen,
wenn zu groß, dann wird das Array nur zum Teil befüllt, der Rückgabewert gibt an wieviel gefüllt wurde
 
PS: Ich könnte zwar einen StringBuffer verwenden, aber hier im Forum wurde mir einmal gesagt, dass der Kompilierer bei "String +=" intern sowies eine StringBuffer-Operation anwendet. - Ist dem doch nicht so?
das stimmt zwar, aber nur zur Hälfte. Natürlich ist jede String-Verkettung ähnlich wie ein StringBuffer implementiert, es bedeutet aber auf deine Ausage bezogen, dass für jede Verkettung ein StringBuffer erstellt wird um zu verketten und danach der String wieder herausgeholt wird.
Du musst also wirklich einen String-Buffer verwenden, sonst wird jede deiner Verkettung in einem neuen String-buffer resultieren, der danach wieder vom Garbage Collector eingesammelt wird.

Die überladenen Methoden read(byte[] b) und read(byte[] b, int off, int len) kann ich leider nicht anwenden (zumindest weiß ich nicht wie...), weil ich die Länge der Datei nicht kenne
man nutzt die überladene Version nicht mit der Zielgröße der Datei sondern mit einer Buffergröße, du erzeugst also z.B. ein Byte-Array mit 8KB (
Code:
new byte[8192]
) und wendest die read-Methode dann mit diesem Buffer so oft an, wie du Daten lesen konntest, dafür ist der Rückgabewert gut 😉
 
Hier mal ein schnelles Beispiel, wie mein Standard zum lesen ausschaut (wenn ich keinen BufferedReader nutze):
Java:
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;

public class SimpleReader
{
    public static void main(String... args)
    {
        try {
            InputStream in=new ByteArrayInputStream("Hello World".getBytes());

            int byteCount=-1;
            byte[] buffer=new byte[2048];
            ByteArrayOutputStream baos=new ByteArrayOutputStream();

            while((byteCount=in.read(buffer))!=-1)
                baos.write(buffer, 0, byteCount);

            System.out.println(new String(baos.toByteArray()));
            
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
}

in.read(buffer) liest die Daten in den buffer (anzahl=größe des arrays) und gibt zurück, wieviele bytes in den buffer geschrieben wurden oder -1 wenn der Stream zuende ist. Mit baos.write(buffer,0,byteCount) schreibe ich dann eben soviele bytes vom buffer in den ByteArrayOutputStream wie auch tatsächlich gelesen wurden. Zum schluss verarbeite ich die bytes (hier zu einem String).
 
Gleich so viele Antworten 🙂
(und genauso viele Lösungsansätze 😀)

das stimmt zwar, aber nur zur Hälfte. Natürlich ist jede String-Verkettung ähnlich wie ein StringBuffer implementiert, es bedeutet aber auf deine Ausage bezogen, dass für jede Verkettung ein StringBuffer erstellt
Das leutet ein - und ist natürlich bei einem Byte-weisen Auslesen der absolute Performance-Super-GAU!
Jetzt klappt es schon recht gut, das mit Byte-Array als Buffer werde ich aber wahrscheinlich auch noch mit einbauen.

Nochmal danke für die Infos!
 

Zurück
Oben