Selbst geschriebener InputStreamReader über einen beliebigen InputStream

megalomaniac

Mitglied
Hallo zusammen,

ich betreibe ein Programm, das XML-Dateien über eine beliebige Schnittstelle (z.B. von Platte oder über einen http-Request) in Form eines InputStreams einliest und weiterverarbeitet. Da bestimmte in diesen Dateien enthaltene Zeichen transformiert werden sollen, habe ich einen XmlInputStreamReader (abgeleitet von InputStreamReader) geschrieben, der diese Transformationsregeln implementiert. Soweit alles okay.

Leider liegen nicht alle XML-Dateien im gleichen Encoding vor. Die Dateien können UTF-8 oder ANSI (ISO-8859-1) kodiert sein. Herausbekommen kann ich das Encoding prinzipiell nur anhand des XML-Headers am Anfang des eingelesenen Datenstroms. Das Encoding muss ich der Superklasse meines XmlInputStreamReaders mitliefern, damit mir der Parser zum späteren Zeitpunkt die Daten des XMLs vernünftig zurückliefert.

Und genau hier liegt mein Problem! Ich muss beim Instanziieren meines XmlInputStreamReaders dem Konstruktor das Encoding mitliefern, zu einem Zeitpunkt, an dem ich noch gar nicht angefangen habe, den Inhalt meines InputStreams auszulesen. Hier mal der passende Quellcode dazu:

Java:
public class XmlInputStreamReader extends InputStreamReader {
	
	(...)
	
    public XmlInputStreamReader(InputStream in, String charsetName) throws UnsupportedEncodingException
    {
        super(in, charsetName);
    }
	
	(...)
}

Ein Henne-Ei-Problem! Ich kann meinen Reader nicht instanziieren, weil ich auf den Inhalt des Streams zugreifen muss, um die notwendigen Encoding-Informationen zu bekommen. Ein nachträgliches Setzen des Encodings ist in der Superklasse InputStreamReader offenbar nicht vorgesehen.

Erschwerend kommt hinzu, dass das Programm relativ performanzkritisch ist. Es werden viele Tausend XML-Dateien (zum Teil mit Volumina > 200 MB) sequentiell verarbeitet. Ich schätze also, dass das naheliegende Vorgehen

1. Stream "anlesen"
2. Encoding ermitteln
3. Stream wegwerfen und neu von der Schnittstelle beziehen
4. XmlInputStreamReader mit jeweiligem Encoding instanziieren

aus Gründen der Performanzerhaltung in meinem Fall nicht zielführend ist.

Lange Rede kurzer Sinn: Fällt jemandem zu meinem Problem eine elegante Lösung ein?
 
> aus Gründen der Performanzerhaltung in meinem Fall nicht zielführend ist.

warum schätzt du das, irgendwas schon dazu getestet? geht es dir nur um abstrakte Punkte wie 'Erzeugen des Stream-Objekte
oder hast du praktische Hinweise wie 'jedes Lesen aus der Schnittstelle kostet sowieso erstmal 3 sec'?

was Java-intern zum Anfordern eines Stream/ Erzeugen eines Objektes an Arbeit anfällt besser selber nicht zu beurteilen versuchen falls nicht unumgänglich

-----

ein Vorschlag: den Stream aus der Schnittstelle cachen, bei kleinen Dateien unter 1 MB vielleicht komplett in ein byte[] einlesen,
darauf dann einen Stream der die ersten bytes liest (ByteArrayInputStream) und mit bekanntem Encoding einen neuen Stream auf das immer noch komplett vorliegende byte[], das wird nicht 'verbraucht',
oder das Encoding direkt aus den bytes feststellen,

inwiefern das Performance-Probleme macht (z.B. zusätzliche 1 MB Arbeitsspeicher), kann man belieblig entscheinden,
komplizierter aber performanter wäre wahrscheinlich doch ein ein eigener Stream, der nur die ersten x bytes cacht
(herausfinden wieviel zur Encoding-Bestimmung benötigt werden),
dieser ersten x bytes eben zur Encoding-Bestimmung einsetzen und danach normal weitergeben, die restlichen bytes direkt durchreichen bzw. aus einen dann mit Encoding initialisieren wirklichen InputStreamReader als Klassenattribut,

(ich sehe gerade Reader statt Stream, überall ruhig byte durch char ersetzen)

auf Methoden der Basisklasse InputStreamReader kannst du dich dann natürlich nicht verlassen, du musst alle wichtigen Methoden überschreiben und direkt aus dem Parameter in lesen,
manche Methoden muss man nicht überschreiben, z.B.

Java:
   public int read(char cbuf[]) throws IOException {
	return read(cbuf, 0, cbuf.length);
    }
kann so bleiben, wenn
> public int read(char cbuf[], int off, int len)
überschrieben ist usw.
 

Neue Themen


Zurück
Oben