Performance Stringoperationen

flashOr

Erfahrenes Mitglied
Hi,
also ich hab ein Performance Problem. Ich muss eine 30MB große Datei einlesen, verarbeiten und wieder schreiben. Das einlesen klappt inzwischen ganz gut, nur leider dauern die Stringoperationen sehr sehr lange. Was könnte ich noch tun um die Performance weiter zu verbessern? Bis jetzt hab ich immer spätestens nach 4h(!) abgebrochen.

Java:
import java.io.*;
import java.net.*;

public class Test {

	FileWriter fw;
	BufferedWriter bw;
	File outFile;
	String myBuffer="";
	
	public static void main(String[] args) {
		Test t = new Test();
		t.read();
	}
	
	public final void read() {
		try {
		    System.out.println("Reading file...");
			BufferedReader in = new BufferedReader(new FileReader("text.txt"));
		    String line;
		    StringBuffer buffer = new StringBuffer();
		    while ((line = in.readLine()) != null) {
		    	buffer.append(line);
		    }
		    line = null;
		    in = null;
		    String buf = addBreak(encUrl(buffer.toString()));
		    write(decURL(buf));
		    
		}
		catch (Exception e) {
			System.err.println("error:"+e);
		}
	}
	

	public final String encUrl(String s) {
		try {
			String oldtest = URLEncoder.encode(s, "ASCII");
			String newTest = oldtest.replace("%3F","");
			newTest = newTest.replace("%00",""); //Null entfernen
			newTest = newTest.replace("%0D%0A",""); //Umbruch entfernen
			myBuffer = myBuffer + newTest;
			oldtest = null;
			newTest = null;
			return myBuffer;
		} 
		catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			return null;
		}
	}

	public final String decURL(String test){
		System.out.println("Decoding...");
		String test2 = "";
		try {
			test2 = URLDecoder.decode(test);
			return test2;
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		}
		return test2;
	}
	
	public final void write(String s) {
		System.out.println("writing!");
		try {
			outFile = new File("my_replaced_test.txt");
			fw = new FileWriter(outFile);
			bw = new BufferedWriter(fw);
			bw.write(s);
			bw.close();
		}
		catch (Exception e) {
			System.err.println("Writing Error:"+e);
		}
	}
	
	public String addBreak(String s) {
		System.out.println("Breaking...");
		int index = 0;
		String startString = "";
		String endString = "";
		startString = s;
		String midString = "";
		for(int i=1;i<s.length();i++) {
			index = startString.indexOf("%7C", index+3);
			if(index == -1 ) {
				return startString;
			}
			if(i%11==0) {
				endString = startString.substring(index+3); 
				midString = startString.substring(index,index+3); 
				startString = startString.substring(0,index); 
				
				midString = midString.replaceFirst("%7C", "%7C%0D%0A");
				startString = startString + midString + endString;
			}
		}
		startString = null;
		endString = null;
		midString = null;
		return startString;
	}
}
 
Kannst du die Datei nicht zeilenweise lesen, die aktuelle Zeile verarbeitung und direkt wieder schreiben?

Eine 30 MB große Datei komplett einzulesen, darauf dann Operationen auszuführen kann ich mir vorstellen dass es langsam ist, sind ja verdammt viele Daten die dann im Speicher liegen.
 
Gar keine so schlechte Idee. Ich werd das mal probieren, vielleicht ists dann schneller.

//edit: Oh, ich seh grad, dann bekomme ich aber ganz große Probleme mit den Umbrüchen.
 
Zuletzt bearbeitet:
Du arbeitest mit sehr vielen String-Objekten.

Vielleicht kannst Du das schon mal reduzieren:
1. bei erster Verwendung initialisieren (also kein String x = "")
2. der "addBreak(...)"-Methode den StringBuffer übergeben und in diesem alles tauschen: myStringBuffer.replace(7,11,"max")
3. so auch in der "encURL(...)" verfahren

Wenn alles nichts hilft: RandomAccessFile benutzen, dort kannst Du zielgerichtet zu einer Stelle innerhalb der Datei springen und Änderungen vornehmen.
 
Ok, mit dem Stringbuffer zu arbeiten scheint performanter zu sein. Aber ich habe noch ein paar Fragen dazu. Methoden wie URLEncoder.encode() benötigen aber einen String. Ist es nicht sehr sehr langsam jedesmal ein Stringbuffer.toString() zu machen? Und die zweite Frage ist es überhaupt möglich in einem Stringbuffer nach einer Zeichenkette zu suchen und nicht nur nach einem einzelnen Zeichen? Denn ich muss ja quasi ein Wort ersetzen und nicht nur ein Zeichen.
 
Ok, mit dem Stringbuffer zu arbeiten scheint performanter zu sein. Aber ich habe noch ein paar Fragen dazu. Methoden wie URLEncoder.encode() benötigen aber einen String. Ist es nicht sehr sehr langsam jedesmal ein Stringbuffer.toString() zu machen? Und die zweite Frage ist es überhaupt möglich in einem Stringbuffer nach einer Zeichenkette zu suchen und nicht nur nach einem einzelnen Zeichen? Denn ich muss ja quasi ein Wort ersetzen und nicht nur ein Zeichen.

Im Zweifelsfall musst du dann eine Suche machen (indexOf) und dann mit delete die Stelle rauslöschen.

Ein Stringbuffer.toString() ist nicht so langsam(glaube ich)...da ein Stringbuffer im Grunde auch nur einzelne chars hat....die Frage ist eher, ob er eine Referenz(die Adresse) oder eine Wertübergabe(das ganze kopieren) macht. Bei einer Referenz ändert er das original mit, welches außerhalb der Methode liegt, bei einer Wertübergabe kopiert er es nur. Aber der Inhalt des Stringbuffers hier würde unangetastet bleiben.
 
Also ich hab jetzt mal auf Stringbuffer umgestellt. Beim ersten Test ist mir gleich aufgefallen das der erste Flaschenhals jetzt diese Stelle ist:

Java:
Stringbuffer s;
URLEncoder.encode(s.toString(), "ASCII")

Also scheint die Funktion toString() doch sehr langsam zu sein, wie ich schon vermutet habe.
Wenn ich nach "%3F" suchen und ersetzen möchte, müsste ich das in einem StringBuffer ja so realisieren:
Java:
s.indexOf("%")
s.indexOf("3")
s.indexOf("F")

Wie ihr schon seht wird das aber zu ein paar klitzekleinen Fehlern führen, denn ich möchte ja nicht alle dreien und alle f aus dem String löschen. Wie kann ich das am besten umgehen?
 
Zuletzt bearbeitet:
Du musst das sicher nicht so machen, weil sonst nur murks rauskommt. Warum suchst du nicht einfach nach der ganzen Zeichenkette s.indexOf("%3F")? Ich sage es nur immer wieder API genau lesen.

Zeig nun bitte den gesamten Code...weil ich sehe immer noch kein Stringbuffer s = new Stringbuffer(20000000) oder sowas in der Art.
 
Oh, Sorry das mit dem indexOf() war wirklich mein Fehler. Ich hatte den String verändert und deshalb kam die Zeichenfolge nicht mehr vor ... :rolleyes:

Hier also mein jetziger Code:
Java:
import java.io.*;
import java.net.*;

public class Test {

	FileWriter fw;
	BufferedWriter bw;
	File outFile;
	
	public static void main(String[] args) {
		Test t = new Test();
		t.read();
	}
	
	public final void read() {
		try {
		    System.out.println("Reading file...");
			BufferedReader in = new BufferedReader(new FileReader("text.txt"));
		    String line;
		    StringBuffer buffer = new StringBuffer(5000000);
		    while ((line = in.readLine()) != null) {
		    	buffer.append(line);
		    }
		    String buf = addBreak(encUrl(buffer));
		    write(decURL(buf));
		    line = null;
		    in = null;
		}
		catch (Exception e) {
			System.err.println("error:"+e);
		}
	}
	

	public final StringBuffer encUrl(StringBuffer s) {
		try {
			System.out.println("Encoding...");
			StringBuffer newTest = new StringBuffer(URLEncoder.encode(s.toString(), "ASCII"));
			System.out.println("Encoding finished, now replacing...");
			int index;
			for(int i=0;i<newTest.length();i++) {
				index = newTest.indexOf("%3F");	
				if(index!=-1) {
					newTest.replace(index,index+3,"");	
				}
				
				index = newTest.indexOf("%00");	
				if(index!=-1) {
					newTest.replace(index,index+3,"");	
				}
				
				index = newTest.indexOf("%0D%0A");	
				if(index!=-1) {
					newTest.replace(index,index+6,"");	
				}
				
			}
			
			return newTest;
		} 
		catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			return null;
		}
	}

	public final String decURL(String test){
		System.out.println("Decoding...");
		try {
			String test2 = URLDecoder.decode(test);
			return test2;
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public final void write(String s) {
		System.out.println("writing!");
		try {
			outFile = new File("my_replaced_test.txt");
			fw = new FileWriter(outFile);
			bw = new BufferedWriter(fw);
			bw.write(s);
			bw.close();
		}
		catch (Exception e) {
			System.err.println("Writing Error:"+e);
		}
	}
	
	public String addBreak(StringBuffer startString) {
		System.out.println("Breaking...");
		int index = 0;
		
		for(int i=1;i<startString.length();i++) {
			index = startString.indexOf("%7C", index+3);
			if(index == -1 ) {
				return startString.toString();
			}
			if(i%11==0) {
				startString.insert(index+3, "%0D%0A");
			}
		}
		return startString.toString();
	}
}
 
Zurück