String Buffer (OUT OF MEMORY)

chocox

Mitglied
Hallo zusammen,

ich lese einen Datenstrom als Byte-Array ein. Dieses Byte Array wandle ich anschließend in Hex um. Und zwar so:

Code:
static String byteArrayToHexString(byte in[]) {

    byte ch = 0x00;
    int i = 0; 
    if (in == null || in.length <= 0)
        return null;

    String pseudo[] = {"0", "1", "2",
"3", "4", "5", "6", "7", "8",
"9", "A", "B", "C", "D", "E",
"F"};

StringBuffer out = new StringBuffer(in.length * 2);

    while (i < in.length) {
        ch = (byte) (in[i] & 0xF0);
        ch = (byte) (ch >>> 4);
        ch = (byte) (ch & 0x0F);    
        out.append(pseudo[ (int) ch]); 
        ch = (byte) (in[i] & 0x0F); 
        out.append(pseudo[ (int) ch]); 

        i++;
    }

    String rslt = new String(out);
    return rslt;

}

Doch leider bekomme ich bei Dateien die über 10 MB sind ein Out Of Memory und zwar auf Grund des String Buffers.
Gibt es eine andere(bessere) Lösung?

Viele Grüße und vielen Dank für eure Hilfe
 
Die Frage ist ob du wirklich den kompletten Dateiinhalt im Speicher zur Verarbeitung benötigst oder würde auch ein Stream von Bytes ausreichen? Auf diese Art kannst du beliebig große Dateien verarbeiten.
 
Ok. Du meinst ich soll einen Stream aufmachen und alles was ich in dem StringBuffer schreibe z. Bsp. in einem ByteArrayOutputStream schreibe und diesen dann weiter verarbeite..?
 
Ich frage mich warum du den Stream von dem du liest erst in ein ByteArray schreibst, statt diesen direkt zu verarbeiten und in deinen StringBuffer (StringBuilder ist übrigens schneller) zu schreiben? Dann hättest du das ganze auch nur einmal im Speicher. Die Frage ist wofür du das Resultat dann benötigst.
 
Ich lese einen AFP-Datenstrom (ähnlich PDF) in ein ByteArray ein. Dieses ByteArray wandle ich dann wie gesagt in Hex um, und hab somit die Möglichkeite den AFP-Datenstrom zu analysieren. Der Aufbau des AFP-Datenstroms ist in sogenannten Tripplets. Zum Beispiel steht das Tripplet X"D3A8A8" für Beginn Dokument. In einem AFP-Datenstrom werden Daten aufbereitet und zu einem Dokument zusammengefügt. Ich hab Content-Daten (XML oder LineData) und sogn. Ressourcen und Anweisungen wie die Daten aufbereitet werden. Ich hoffe die Erklärung ist einigermaßen verständlich. Also es geht darum diesen AFP-Datenstrom zu analysieren. Deshalb das ByteArray.
Das mit dem Stream ist mir jetzt klar, aber ich will nicht in eine Datei schreiben, sondern müsste den Stream an ein Ziel (StringBuffer oder StringBuilder) weitergeben, damit ich ihn anschließend weiter verarbeiten kann.
Und da harperts.
 
Dann wandle den Datenstrom am besten sofort beim Lesen in einen Syntaxbaum oder ähnliches mit den entsprechenden Objekten um. Du brauchst dann nur einen kleinen Ausschnitt zu buffern.
 
Hallo,

ich hätte hier gar keinen StringBuffer verwendet, sondern gleich beim Einlesen den Datenstrom analysiert. Beispielsweise mit einem InputStreamFilter:
Java:
package de.tutorials;

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

public class HexReaderFilterExample {
	public static void main(String[] args) throws Exception {
		AFPInputStream afpInputStream = new AFPInputStream(new FileInputStream("/tmp/someFile"));
		System.out.println(afpInputStream.read());
		afpInputStream.close();
	}

	static class AFPInputStream extends FilterInputStream {
		protected AFPInputStream(InputStream in) {
			super(in);
		}

		//analyze input stream in read(...) Methods
	}

}

Gruß Tom
 
Also ich habe jetzt probiert den Stream direkt in Hex umzuwandeln und das funktioniert auch, aber wenn ich dann daraus einen String machen will, weil ich in diesem nach den Tripplets suche, kommt jedesmal eine "Exception in thread "main" java.lang.OutOfMemoryError: Java heap space." Ich gebe bereits den Parameter -XX:+AggressiveHeap mit, der der JVM sagt, sie soll sich so viel Arbeitsspeicher schnappen wie möglich.
Ich lese folgendermaßen den Stream ein und wandle ihn in Hex um:
Code:
	public static String getFile(File file) throws IOException{

		final ByteArrayOutputStream bo;	
		final long length = file.length();
		byte ch = 0x00;
		
		if(length < Integer.MAX_VALUE){
			bo = new ByteArrayOutputStream((int) length);
			bo.flush();
			
		}
		else
		{
			bo = new ByteArrayOutputStream(Integer.MAX_VALUE);
		}
		
		final FileInputStream in = new FileInputStream(file);
		int c;
		while((c = in.read()) != -1){
			
			
			String pseudo[] = {"0", "1", "2",
					 "3", "4", "5", "6", "7", "8",
					 "9", "A", "B", "C", "D", "E",
					 "F"};

		     ch = (byte) (c & 0xF0); // Strip off
		     ch = (byte) (ch >>> 4);
		     ch = (byte) (ch & 0x0F);  
		     
		     bo.write((pseudo[ (int) ch]).getBytes());
		     ch = (byte) (c & 0x0F); 
		     bo.write((pseudo[ (int) ch]).getBytes());
		    		
		}

		String s = bo.toString(); //Da wirft er jedesmal den Fehler
		in.close();
		bo.close();
		
		return s;	
	}

Anschließend möchte ich in diesen ewig langem String nach dem Muster X'D3A8A8 suchen, aber das kann ich nicht. Wie das mit dem FilterStreamInput funktionieren soll kann ich mir gerade nicht erklären.

Vielen Dank für eure Hilfe.
 
Zuletzt bearbeitet:
Es kann halt immer problematisch werden eine ganze Datei in den speicher zu laden. Zur Optimierung könntest du dir noch überlegen ob es nicht ausreichend ist einzelne Fragmente (ähnlich wie beim Sax parsen) einzuladen und zu verarbeiten. Kenne jetzt nicht deine Dateistruktur aber ein solche Fragment könnte dann eines deiner Tripplets sein...?
Falls das auch nicht hilft gibt es noch die Möglichkeit die Datei einmal zu Indexen, d.h. einmal komplett durchzu-streamen und wichtige Positionen zu markieren. Danach bei der Abarbeitung kannst du dann via RandomAccessFile die Daten bei diesen Positionen lesen um so nur wirklich relevante Daten im Heap zu halten, oder du lädst die Datei gleich in eine DB und kannst die analyse dann mit hilfe von SQL durchführen.

vg,
ck.
 
Zurück