Datei wird nicht vollständig geschrieben (FileOutputStream, OutputStreamWriter, Encoding)

mla.rue

Bekanntes Mitglied
Hoi,
folgendes Problem: obwohl die verwendete Liste, welche die Zeichen zum Schreiben beinhaltet vollständig und richtig ist, so werden in die neu zu erstellende Datei nur ca 99% der Zeilen geschreiben, 10 Zeilen vor tatsächlichem Ende wird mitten in einer Zeile ein "cut" gemacht. Wo liegt der Hund begraben?

Aufgabe: ich muss eine CSV Datei einlesen und bereinigen (Linux)
Datei Encoding: unrecognized 8-bit

Glücklicherweise liegen alle "gültigen" Zeichen im ISO-8859-15 an den richtigen Stellen, Datei wird so eingelesen:

Java:
    void bereinigeDatei{
	String strCSVDatei = "/home/datei.csv";
	String strEncoding = "ISO-8859-15";
	String strTmp = "";
	FileInputStream fis = null;
	InputStreamReader in;

	try {
	    fis = new FileInputStream(strCSVDatei);
	    in = new InputStreamReader(fis, strEncoding);

	    for (int c; (c = in.read()) != -1;) {

		//gültige zeichen A-Za-z0-9ÄÖÜäöü,.-;newLine
		if (c == 10 || c == 32 || c == 44 || c == 45 || c == 46 || c == 48 || c == 49 || c == 50 || c == 51
			|| c == 52 || c == 53 || c == 54 || c == 55 || c == 56 || c == 57 || c == 59 || c == 65
			|| c == 66 || c == 67 || c == 68 || c == 69 || c == 70 || c == 71 || c == 72 || c == 73
			|| c == 74 || c == 75 || c == 76 || c == 77 || c == 78 || c == 79 || c == 80 || c == 81
			|| c == 82 || c == 83 || c == 84 || c == 85 || c == 86 || c == 87 || c == 88 || c == 89
			|| c == 90 || c == 97 || c == 98 || c == 99 || c == 100 || c == 101 || c == 102 || c == 103
			|| c == 104 || c == 105 || c == 106 || c == 107 || c == 108 || c == 109 || c == 110 || c == 111
			|| c == 112 || c == 113 || c == 114 || c == 115 || c == 116 || c == 117 || c == 118 || c == 119
			|| c == 120 || c == 121 || c == 122 || c == 196 || c == 214 || c == 220 || c == 228 || c == 246
			|| c == 251) {
		    strTmp = strTmp + (char) c;

		}

		if (c == 10) {
		    alCSVZeile.add(strTmp);
		    strTmp = "";
		}

	    }
	} catch (IOException ex) {
	    javax.swing.JOptionPane.showMessageDialog(null, ex, "IOException", 0);
	} finally {
	    try {
		fis.close();
	    } catch (Exception ex) {
		javax.swing.JOptionPane.showMessageDialog(null, ex, "Exception", 0);
	    }
	}

    }

gibt man sich jetzt meine Liste (alCSVZeile) aus, ist alles schön, sauber, vollständig enthalten. Von der ersten, bis zur letzten Zeile. Unerwünschte Zeichen sind alle weg. Gewünschte (Umlaute ß.,;- Buchstaben und Zahlen und das NewLine an der richtigen Stelle) sind alle ordentlich drin.

Auszug der Ausgabe (der erstellten Liste, die letzten 14 Zeilen):
ZU ;ZU ;fach;name;strasse;plz ort;tel;
ZUB ;ZUB ;fach;name;strasse;plz ort;tel;
ZUBERA;ZUBERA;fach;name;strasse;plz ort;tel;
ZUCH ;ZUCH ;fach;name;strasse;plz ort;tel;
hier gehts noch 10 Zeilen weiter, dann Listenende

aber das wird in die Datei geschrieben:
ZU ;ZU ;fach;name;strasse;plz ort;tel;
ZUB ;ZUB ;fach;name;strasse;plz ort;tel;
ZUBERA;ZUBERA;

Folgende Methode übernimmt das Abspeichern:
Java:
    private void schreibeDateiNeu{

	String strCSVDatei = "/home/dateineu.csv";
	String strEncoding = "ISO-8859-15";

	FileOutputStream fos = null;
	OutputStreamWriter out;

//        //ausgabe inhalt Liste
//	for (int i = 0; i < alCSVZeile.size(); i++) {
//	    System.out.println(alCSVZeile.get(i));
//	}

	try {
	    fos = new FileOutputStream(strCSVDatei);
	    out = new OutputStreamWriter(fos, strEncoding);
	    out.write(alCSVZeile.get(0));
	    for (int i = 1; i < alCSVZeile.size(); i++) {
		out.append(alCSVZeile.get(i));
	    }
	    javax.swing.JOptionPane.showMessageDialog(null, "fertig", "Info", 1);
	} catch (IOException ex) {
	    javax.swing.JOptionPane.showMessageDialog(null, ex, "IOException", 0);
	} finally {
	    try {
		fos.close();
	    } catch (IOException ex) {
		javax.swing.JOptionPane.showMessageDialog(null, ex, "IOException", 0);
	    }
	}
	System.out.println("dateineu.csv geschrieben");
    }

Weiss nicht wo ich nach einem Fehler suchen soll. Denkanstöße (oder Lösungen) wären ne feine Sache 🙂.
 
Die fehlenden Daten liegen noch im Buffer vom OutputStreamWriter. Schließ nach dem schreiben einfach den OutputStreamWriter statt den FileOutputStream. Der kümmert sich dann afaik auch darum das der OutputStream geschlossen wird.
 
@Lumaraf: Da liegst du richtig.

Das kann man sich eigentlich als Merksatz einrahmen und neben seinem Kurszertifikat an die Wand hängen.
Streams, die bei der Instanzierung einen weiteren Stream benötigen, mit anderen Worten geschachtelt sind, sollten stets "close()" des parent streams aufrufen und zwar nachdem die letzten Daten an diesen weitergereicht wurden. Betreffende Standard-JVM-Streams machen das alle.
 
Erstens: Miserabler Code (tut mir leid, das so dreist zu sagen...) aber guter Programmierstil ist ziemlich wichtig, vor allem, wenn du Hilfe erwartest.

Korrekte Einrückung und vor allem deine if-Abfrage im ersten Code-Schnipsel zeugt nicht gerade von Eleganz.

Ich habe mir mal die Mühe gemacht, das zu ordnen:

Java:
void bereinigeDatei() {
	String strCSVDatei = "/home/datei.csv";
	String strEncoding = "ISO-8859-15";
	String strTmp = "";
	
	try( FileInputStream fis = new FileInputStream( strCSVDatei );
		InputStreamReader in = new InputStreamReader( fis, strEncoding ) ) {
		
		for( char c; ( c = (char) in.read() ) != -1; ) {
			
			if( c == 10 || c == 32 /* new line and space */
				|| c >= 44 && c <= 59 /* digits and ,.-/;: */
				|| c >= 65 && c <= 90 /* upper case alphabet */
				|| c >= 97 && c <= 122 /* lower case alphabet */
				|| c == 196 || c == 214 || c == 220 || c == 228 || c == 246 || c == 251 ) { /* umlaute */
				strTmp += c;
			}
			
			if( c == 10 ) {
				alCSVZeile.add( strTmp ); /* Hinweis: das Komma wird dem String angefügt */
				strTmp = "";
			}
		}
	} catch( IOException ex ) {
		javax.swing.JOptionPane.showMessageDialog( null, ex, "IOException", 0 );
	}
}

Java:
private void schreibeDateiNeu() {
	
	String strCSVDatei = "/home/dateineu.csv";
	String strEncoding = "ISO-8859-15";
	
	// //ausgabe inhalt Liste
	// for (int i = 0; i < alCSVZeile.size(); i++) {
	// System.out.println(alCSVZeile.get(i));
	// }
	
	try( FileOutputStream fos = new FileOutputStream( strCSVDatei );
		Writer out = new OutputStreamWriter( fos, strEncoding ) ) {
		for( int i = 0; i < alCSVZeile.size(); i++ ) {
			out.write( alCSVZeile.get( i ) );
		}
		javax.swing.JOptionPane.showMessageDialog( null, "fertig", "Info", 1 );
	} catch( IOException ex ) {
		javax.swing.JOptionPane.showMessageDialog( null, ex, "IOException", 0 );
	}
	System.out.println( "dateineu.csv geschrieben" );
}

Jetzt kann man schon viel mehr damit anfangen.

Falls du nicht Java 7 verwendest, geht try-with-resource noch nicht. Da müsstest du auf deine alte Methode zugreifen. Außerdem solltest du im finally-Block überprüfen, ob die Streams, die du schließt, nicht
Code:
null
sind.
Ansonsten lies oben.

Kleine Frage: Ist Carriage Return (\r / 13) absichtlich nicht drin? Der gehört nämlich zum Line Separator in Windows und Mac.
 
Zuletzt bearbeitet:
Pfffffffffft 🙂 MiserablerCode... wo denn? Sehe nur bei der if-Abfrage ein "Problemchen", und ob strTrmp += c soviel eleganter ist? Ich fand die Schreibewiese schon immer "blöd" und da der CodeStyleGuide der Firma eh die lange Schreibweise verlangt, sehe ich da kein Problem 🙂.

Sorry wegen der Formatierung, in der Vorschau war noch alles richtig. Da sich das Problem aber dank Lumaraf nach 14min erledigt hat, sah ich keine Notwendigkeit mehr den Eröffnugnspost zu editieren 😉.

Das mit CarriageReturn ist Absicht, Kunden haben nur Linux, das Programm welches die csv Erstellt läuft nur unter Linux, das Programm, das die bereinigte CSV später wieder einliest läuft nur unter Linux und somit muss die Bereinigung nur unter Linux laufen.
 
Wie wärs mal mit
Java:
Character.isDigit(...)
Character.isLetter(...)
Character.isSpace(...)
Character.isLetterOrDigit(...)
....
 

Zurück
Oben