Java ArrayIndexOutOfBoundsException beim einlesen einer großen Datei

Titania

Cadet 3rd Year
Registriert
Nov. 2012
Beiträge
40
Guten Tag,

Ich entwickle einen kleinen MP3-Player mit einer Bibliothek als Projekt für die Schule, in die Titel eingespeichert werden können. Die Datenbank liegt in einer csv-Datei (SQL nicht erlaubt). Ich habe eine Datenbank mit etwas über 20.000 Einträgen erstellt. Will ich diese jetzt einlesen erhalte ich folgenden Fehler:

Code:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 2
	at player.components.ImportExport.Import(ImportExport.java:141)

Der Code, an der der Fehler stattfindet ist folgender (bei part2):

Code:
private Playlist Import(String Filename){
	//Importiert eine Playlist mit dem vorgegebenen Dateinamen
	
	BufferedReader reader = null;							    //erstellen des "Datei-Lesers"
	try {
            PrintWriter writer = new PrintWriter("debugfile.csv", "UTF-8");
	    reader = new BufferedReader(new FileReader(Filename));			    //dem "Datei-Leser" wird die datei übergeben
	    String zeile = null;							    //die nötigen Variablen werden erstellt
	    String[] parts = null;
	    Playlist einePlaylist = new Playlist();
            int counter = 0;
	    while ((zeile = reader.readLine()) != null) {				    //jede Zeile wird nacheinander ausgelesen und dann in die einzelnen
		parts = zeile.split(";"); 						    //Teile zerschnitten
                writer.println(counter + ";" + zeile + ";");
                counter = counter + 1;
		//csv Format: Index;TitelName;Dauer;Interpret;Album;Pfad		    //Die einzelnen Teile werden dann jeweils einem Titel in der 
                String part1 = parts[1];
                String part2 = parts[2];
                String part3 = parts[3];
                String part4 = parts[4];
                String part5 = parts[5];
                einePlaylist.addTitle(part1, part2, part3, part4, part5);    //Playlist zugewießen
	    }
            writer.close();
            return einePlaylist;							    //Die Playlist wird zurückgegeben
	} catch (FileNotFoundException ex) {						    //eventuelle Fehler werden in den catch-Blöcken abgefangen
	    Logger.getLogger(ImportExport.class.getName()).log(Level.SEVERE, null, ex);
	} catch (IOException ex) {
	    Logger.getLogger(ImportExport.class.getName()).log(Level.SEVERE, null, ex);
	} finally {									    //am Schluss wird die Datei geschlossen
	    try {
		reader.close();
	    } catch (IOException ex) {							    //Fehler werden hier abgefangen
		Logger.getLogger(ImportExport.class.getName()).log(Level.SEVERE, null, ex);
	    }
	}
	return null;									    //Im Falle eines Fehlers wird "null" zurückgegeben
    }

Der Fehler tritt beim 19683. Eintrag auf. Dort scheint der String "zeile" einfach mittendrin abgehackt zu sein.
Code:
19683	19683	Keine Informationen vorhanden	mm:ss	Keine Informationen vorhanden	Keine Informationen vorhanden	C:\Musik\alt\Music\Music\Unknown Arti
Aus irgendeinem Grund stimmt hier auch der Index nicht ganz. Laut Excel ist dies die 19653. Zeile, der Counter hat aber schon bis 19683 gezählt. Dieser Fehler hat sich aber schon im 1 stelligen 1000er-Bereich eingeschlichen. (Dies ist aber in der Bibliothek-Datei genauso)

Ich versuche nun seid ein paar Stunden die Ursache für diesen Fehler zu finden, was mir aber leider nicht möglich war, deshalb hoffe ich nun, dass ihr mir helfen könne :D



Das Schema, nach welchem die Daten in der Bibliothek.csv abgespeichert sind ist folgendes:
Code:
0;Boom (How You Like That);mm:ss;hed p.e.;Broke;C:\Musik\alt\Music\Music\(hed) pe\Broke\08 Boom (How You Like That).mp3
1;;mm:ss;;-- xrnb.net --;C:\Musik\alt\Music\Music\Unknown\Unknown\Beyonce - Sweet Dreams (Barakvanunu.mp3
2;Dreamscape;mm:ss;009 Sound System;009 Sound System;C:\Musik\alt\Music\Music\009 Sound System\009 Sound System\01 Dreamscape.mp3
3;Space And Time;mm:ss;009 Sound System;009 Sound System;C:\Musik\alt\Music\Music\009 Sound System\009 Sound System\02 Space And Time.mp3
Dieses ist in der kompletten Bibliothek identisch.
 
Zuletzt bearbeitet:
So, nach einem langen Beitrag von mir hat CB gerade ihre Server neu gestartet. Daher fasse ich mich beim zweiten Anlauf kürzer ;)

Schnell umrissen: Schritt für Schritt nach Fehlern suchen. So würde ich zumindest in deiner Situation vorgehen.

1. Ist mit der Quelldatei alles in Ordnung?
Passt die Ausgabe zu dem, was in der Datei steht?

PHP:
private void importFile(String fileName) throws FileNotFoundException, IOException {


    try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {

        int i = 1;
        String line;

        while ((line = reader.readLine()) != null) {
            System.out.println(String.format("%05d >>> %s", i, line));
            i++;
        }
    }
}

2. Hat wirklich jede Zeile die von dir erwartete Anzahl an Informationen?

PHP:
private void importFile(String fileName) throws FileNotFoundException, IOException {

    try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {

        int i = 1;
        String line;
        String[] aSplit;

        while ((line = reader.readLine()) != null) {

            aSplit = line.split(";");

            if (aSplit.length != 6) {
                System.out.println("Fehlerhafte Arraylänge bei Index " + i);
                break;
            }

            i++;
        }
    }
}

Wenn das alles korrekt durchläuft, kannst du dich gerne wieder melden.
Ansonsten bleibt noch anzumerken, dass du dir die Konvention zur Namensgebung von Objekten nochmal anschauen solltest. Großgeschriebene Methodennamen oder Parameter sind zu vermeiden. Besonders Wörter wie "Import" nicht benutzen.
 
Zuletzt bearbeitet:
Vielleicht enthält ein Eintrag auch ";" als nicht Trenner. Im Code so direkt seh ich jetzt keinen Fehler, hätte jedoch den einen oder anderen Verbesserungsvorschlag.

1. Verwende bitte Java 7 features (6 würd ich nimmer Supporten mittlerweile sind wir bei 8)
- Closable Resources -> sparst dir das finally
- Multicatch -> wobei in dem Fall egal du könntest catch FinalNotFoundException einfach streichen weils von IOException erbt
2. Logger auf Class level deklarieren
3. part1,part2,.. solltest du nicht als Variabelnnamen verwenden -> Dann hast gratis Code dokumentation
4. Wie schon gesagt Java convention sind Methodennamen die mit lower case beginnen; import is glaub ich nur für package namen verboten
5. String[] parts = null außerhalb der while bringt dir nix -> Ist einfach eine redundante Zeile Code
6. counter = counter + 1; -> Sollte in meinen Augen ans Ende der while und replace es mit counter++;
7. Im Fall einer Exception wird derzeit dein writer nicht geschlossen
8. Würd einen Check einbauen, dass eine Zeile kein unerlaubtes ";" enthält.
9. Nachdem du eine Diskrepanz zwischen Index und Zeilenanzahl hast, kannst du das ja auch überprüfen.
 
Es wurde eh schon fast alles gesagt. Vorallem solltest du nach dem Einlesen einer Zeile erst mal prüfen, ob der Inhalt deinen Anforderungen gerecht wird, d. h. schau ob was drin ist und ob es 6 Teile sind (siehe Killkrog)! Wenn nicht, dann gib die fehlerhafte Zeile aus und mache mit der nächsten weiter.

Es kann auch sein, dass unerwünschte Zeilenumbrüche in der csv-Datei drin sind. Das würde die unterschiedliche Zeilenzahl von deinem Zähler erklären.
Mit welchem Programm editierst du das csv? Exportierst du wirklich mit UTF-8?

Schau dir das csv mal mit notepad++ an (im Menü bei Kodierung auf UTF-8 umschalten). Dann gehst du zur besagten Zeile und schaust, ob die Zeilennummern passen.
 
Zuletzt bearbeitet:
Wenn du das ganze richtig robust machen willst, brauchst du eine Parser, der dir valides CSV lesen kann.
Das ist in deiner Lösung nicht gegeben, da du bspw. readLine verwendest, was keine Ahnung von CSV hat. Das fliegt dir dann um die Ohren, wenn du Zeilenumbrüche in den Titelnamen oder so hast.
Ich denke, du wirst ganz einfach gültiges CSV haben, was aber falsch/gar nicht geparsed wird. Mit split() bekommt man fast nie eine wirklich robuste Lösung hin, daher würde ich Lösungen, die darauf aufbauen vermeiden.

In der Regel kannst du Parser so schreiben, dass du immer nur 1 Byte aus deinen Stream ließt und dieses auswertest (du kannst Abkürzungen nehmen, wenn du dann Regeln hast wie "lese so lange, bis du 'x' findest")).

Falls du noch mehr CSV-Dateien verwenden willst, lohnt sich dann das Kapseln in eine Klasse.
 
Danke für die schnellen und ausführlichen Antworten.

Leider werde ich wohl erst am Wochenende Zeit habe, an dem Projekt weiterzuarbeiten, auf ein paar Vorschläge kann ich aber direkt antworten.
@Funart
1. JLayer (der verwendete Player) will bei mir nicht mit java 7 kompilen. Da ich nach den Vorgaben auch Java 8 nutzen darf hatte ich mich dazu entschieden dies einfach mal dabei zu belassen.
Das mit deinen Vorschlägen zu try-catch werden ich mir anschauen (hatte bisher einfach das auto-generierte von Netbeans genommen)
2. ok, mach Sinn ;) (bisher auch von Netbeans autegeneriert)
3. das hatte ich nur reingemacht, um die genaue Stelle, an der der Fehler auftritt, leichter finden zu können. (vorher wars ganz normal der array)
4. Auch das schau ich mir nochmal an. Für mich ist es immer ein Problem, zu wissen, welche Namen jetzt verboten sind oder nicht. Gibts da irgendeine liste?
5. Bringt mir das nicht einen Performancevorteil, wenn ich das außerhalb deklarier, denn dann muss ich den Array nicht jedesmal während der Schleife neu initialisieren?!
6. wird gemacht ;). gibts zwischen counter = counter +1 und counter++ noch einen unterschied, außer dass es (in diesem Fall) eine andere Schreibweiße ist?
7. Im Falle einer Exception wird er doch dann gar nicht erst geöffnet?! erstellt wird der writer ja auch im try-Block?
8. Ok werde ich ins Programm implementieren

zu 8. und 9. & @ Killkrog.....werde ich mir am WE ein kleines Tool zum simplen überprüfen der Bibliothek schreiben, welches sie auf die ganzen angesprochenen möglichen Fehlerquellen überprüft. Melde mich hier dann mit den Ergebnissen


@wahli
Editieren tu ich die csv gar nicht. Nur in dem Player. Das einzige was ich extern gemacht habe, ist sie in excel zu öffnen, da excel das csv-Format übersichtlich anzeigt (Erstellt wird die Bibliothek-Datei auch mit dem Player). Dort hatte ich auch durch einen Sichttest und suchttest (strg + f) eine einfache Fehlersuche gemacht.
Ich werde den UTF-8 - Parameter zur Sicherheit mal noch mit anhängen.


Das kuriose ist ja, dass es mitten im Text abbricht. Wie in meinem ersten Beitrag zu sehen mitten im Wort "Artist". Habe an dieser Stelle schon nachgeschaut, dort ist definitiv kein Semikolon.
Für mich sieht es fast danach aus, als würde irgendetwas überlaufen. Da ich die Bibliothek-Datei gerade leider nicht da habe, kann ich keine genauen Werte sagen, aber ich meine mich zu erinnern, dass es bis zu besagten zeile (19xxx) etwa 2,6 Millionen Zeichen waren (gezählt von notepad++), was noch unter dem String limit liegt.


Der Fehler tritt übrigens erst auf, seid ich ID3Tags implementiert habe. Bei Interpret und Album standt vorher einfach "Unbekannt" und beim Titelnamen der Dateiname. Seid der Implementierung der Tags ist die Datei also zeichenmäßig länger geworden.

@Hancock
Hört sich durchaus interessant an ein komplett eigenen Parser zu schreiben. Wenn mir die Zeit am Wochenende noch reicht, werde ich es gleich mal machen, alleine schon um es mal gemacht zu haben.
Am besten sollte ich mit einem FileInputStream arbeiten oder?!
 
Zuletzt bearbeitet:
Hmm, andere Idee gerade...
Kann es sein, dass in der Zeile, wo der Fehler passiert und "abgeschnitten" wird ein Zeilenumbruch ist, der von Excel nicht ausgewertet wird, aber von Java schon? (\r, \n, \r\n)
 
Zurück
Oben