Lesen von sehr großen dateien

  • Themenstarter Themenstarter Guest
  • Beginndatum Beginndatum
Status
Nicht offen für weitere Antworten.
G

Guest

Gast
hallo,
ich möchte eine etwa 3 MB große CSVdatei einlesen,bestehend aus etwa 200.000 Zeilen und 3 Spalten einlesen

Folgende readFile Methode,die auch funktioniert:

Code:
public boolean readFile(String filename) {
		// alle zeilen löschen,falls schon was da war
		rows = new ArrayList<Row>();
		String zeile = "";
		String split[] = null;
		int zeilenNummer = 0;
		boolean isInsert = false;
		try {
			FileReader file = new FileReader(filename);
			BufferedReader data = new BufferedReader(file);
			// zeilenweise lesen
			while ((zeile = data.readLine()) != null) {
				// neues zeielnobjekt
				Row myZeile = new Row(++zeilenNummer);
				split = zeile.split(",\""); // Feldtrenner

				for (int i = 0; i < split.length; i++) {

					// leere Zeile ignorieren
					if (!split[i].equals("")) {
						// eventuell Leerzeilen zwischen zwei ; entfernen
						// und Spalten ins zeilenobjekt packen
						// die erste Zeile enthaelt immer die Spaltenbezeichner
						isInsert = myZeile.insertColumn(split[i].replaceAll("\"", ""));
					}
				}
				// Zeile in die zeilen-liste
				isInsert = this.rows.add(myZeile);
			}
		} catch (FileNotFoundException e1) {
			System.out.println("");
			System.out.println("Datei existiert nicht");
			isInsert = false;
		} catch (IOException e2) {
			System.out.println("");
			System.out.println("Fehler beim Lesen der Datei");
			isInsert = false;
		}
		return isInsert;
	}

Das Problem:ich benötige für das einlesen der 3Mb großen datei etwa zwsichen 4-5 Sekunden (P4 2,6 Ghz und 256MB Ram) Was ziemlich lange ist,wie ich meine. die meiste Zeit etwa 3 Sekunden brauche ich für:
isInsert = myZeile.insertColumn(split.replaceAll("\"", "")); -->replaceAll schluckt etwa 2 Sekunden !!!!!!!!!!!

Hat jemand eine Idee warum das sol lange dauert,bzw. einen Vorschlag wie man es schöner/schneller machen könnte

das replaceAll brauche ich,um Hochkommatas zu entfernen,die sollen nicht mit gespeichert werden,stehen in der CVS datei als Texttrenner

eine zeile sieht etwa so aus: "Spalte1","Spalte2","Spalte3"

Gruß
und danke für eure Hilfe
sky

*codetags eingefügt*
-Wildcard-
 
Wenn es extrem auf Geschwindigkeit ankommt, kann man vieles von dem, was dort mit den einzelnen Zeilen gemacht wird, "per Hand" machen (etwas umständlicher, aber ggf. effizienter). Das replaceAll ist da - siehe maki - schon ein heißer Kandidat. Evtl. könnte man auch das "split" per Hand nachbauen, aber man müßte im Profiler nachsehen und ggf. teste, ob sich das lohnt. Ein Kleinigkeit wäre vielleicht noch sowas wie
rows = new ArrayList<Row>(anzahlZeilen);
wenn man die Anzahl vorher schon kennt.

Aber weswegen ich eigentlich antworte 😀 : Das isInsert wird vermutlich (!) nicht das machen, was es soll. Es bezieht sich ja immer nur auf die letzte Operation (und insbesondere liefert rows.add(..) IMMER true).
 
Hi

Einwand durchaus korrekt,

wobei isInsert im tryBlock eigentlich immer true zurück gibt:

in der Klasse Row:
ArrayList columns=null;
public Row() {

this.columns = new ArrayList<String>();
}
public boolean insertColumn(String column) {
return this.columns.add(column); //--->add:gibt immer true sollte es,wenn alles richtig initialisiert ist und das ist es

}

von daher gibt das isInsert immer true zurück wenn keine Exception ausgelöst wurde,wir uns also im try Block befinden,ansonsten ist isInsert immer false im catch Block

macht also doch Sinn.

man könnte im try Block auch direkt isInsert=true setzen,aber wenn meine Methoden schon einen Rückgabewert haben,dann kann man diesen auch auswerten...das war meine Absicht dahinter.

Du hast aber Recht : isInsert = myZeile.insertColumn(split.replace("\"", "")); bezieht sich auf die letzte Operation.Aber ich gehe mal davon aus,wenn irgendwo ein false auftritt beim insertColumn,dann überall,sonst ist es immer true....


Oder ist das nicht schöner Programmiestil.
Bin für Vorschläge immer offen


zum eigentlichen Problem:

achso nur String replace() statt replaceAll hat nur minimalen Einfluss auf die geschwindigkeit
Merwürdig ist auch,dass ich beim ersten Kompilieren bei 6,7,8, oder gar 9 Sekuden liege.nach dem 3./4./5.Start dann bei 4-5 Sekunden. hat jemand eine Idee,warum der große Unterschied von ca.2-4 Sekunden ????

lieben Gruß

und danke euch
 
Anonymous hat gesagt.:
man könnte im try Block auch direkt isInsert=true setzen,aber wenn meine Methoden schon einen Rückgabewert haben,dann kann man diesen auch auswerten...
Nicht wenn dadurch eine Bedeutung für isInsert suggeriert wird, die es nicht hat! :shock: Wenn ich das jetzt richtig verstanden habe, soll es ja quasi "zurückgeben, ob keine Exception aufgetreten ist". Das KANN zwar OK sein, aber mönnte man eindeutiger und verständlicher schreiben als
Code:
try
{
....
}
catch (...Exceptions )
{
    return false;
}
return true;

achso nur String replace() statt replaceAll hat nur minimalen Einfluss auf die geschwindigkeit

Ja, vermutlich wäre es eben schneller, das per Hand zu machen. Man weiß jetzt auch nicht, was "insertColumn" genau macht, aber wenn DAS nicht zeitkritisch ist, könntest du ggf. sowas machen wie
Code:
zeile = ...
Row row = new Row(++zeilenNummer);
String currentToken = "";
for (int i=0; i<zeile.length(); i++)
{
    if (zeile.charAt(i) == '\"' || zeile.charAt(i) == ',') // Feldtrenner
    {
        if (currentToken.length() > 0)
        {
            row.insertColumn(currentToken);
        }
        currentToken = "";
    }
    else // Jetzt gilt auch immer: if (zeile.charAt(i) != '\"') // "replaceAll"
    {
        currentToken += zeile.charAt(i);
    }
}
if (currentToken.length() > 0)
{
    row.insertColumn(currentToken);
}
Man könnte da evtl. NOCH mehr tweaken, wenn man mit einem StringBuffer oder einen char-Array arbeitet, aber vermutlich hängt das dann auch davon ab, wie lang die Zeilen sind usw.
 
Hallo
danke dir..zur isInsert Geschichte hast du mich überzeugt, ist eine gültige Begründung!! Werd ich wohl so machen

und zur Performace-geschichte..danke für den Tip..werd ich später mal ausprobieren...
 
ich würd schon String#split verwenden, um mir den string zu zerlegen. aber auf String#replace/replaceAll kann man bei der vorgabe vollständig verzichten und String#substring verwenden. das ist wesentlich schneller und man die meisten string-vergleiche weglassen.

Code:
// halb pseudo
String[] tokens = line.split( ";" );
for( String token : tokens )
{
   token = token.trim();
   if( token.length() > 2 )
   {
      // zwei quotes + mind. 1 zeichen => länge größer 2 = token hat inhalt
      token = token.substring( 1, token.length() - 1 );
      // add token
   }
}
 
Hmnaja - String#spilt verwendet ja auch Regular expressions. Und die SIND teuer. Speziell wenn es nur um einzelne Zeichen geht, an denen gesplittet wird (bzw. bei replace/replaceAll nur einzelne Zeichen weggelassen werden sollen) kann man sehr performant beides "in einem Rutsch" erledigen, indem man einfach einmal durch den String läuft, und mit "elementaren String-Operationen" alles macht, was gemacht werden muss.
 
Hallo,

großes KINO Marco13 :applaus: :applaus: :applaus: :applaus:

Habe deinen Code ausgetestet und siehe da gegenüber split und replace Varainte ca. 2 sekunden Ersparnis..lieg nun beim einlesen einer ca:3MB großen datei bei 2 Sekunden.vorher waren es ca: 4 Sekunden.

Und mein Rechner ist dann noch nicht ganz der schnellste..auf einem Rechner mit Intel Core2Quad Prozessore (2.4
bzw. teilweise 2.66 GHz) bin ich bei der ersten Variante mit split und replace schon bei 2 Sekunden gewesen....😀

Echt Klasse.

Danke für eure coolen Tipps.

:toll: :applaus: :toll:

lieben Gruß

und DANKE,hoffe kann auch mal so schöne Tips geben,aber daran arbeite ich.
 
Status
Nicht offen für weitere Antworten.

Zurück
Oben