InputStreamReader Pufferproblem

Ich lese Daten aus einer Datei, die leider zwei Arten der Zeichenkodierung aufweist.

Es gibt einen Header und einen Body. Der Header ist immer in ASCII und definiert den Zeichensatz, in dem der Body codiert ist.

Der Header hat keine feste Länge und muss durch einen Parser ausgeführt werden, um dessen Inhalt / Länge zu bestimmen.

Die Datei ist möglicherweise auch sehr groß, daher muss vermieden werden, dass der gesamte Inhalt in den Speicher verschoben wird.

So fing ich mit einem einzelnen InputStream an. Ich wickle es anfangs mit einem InputStreamReader mit ASCII ein und decodiere den Header und extrahiere den Zeichensatz für den Body. Alles gut

Dann erstelle ich einen neuen InputStreamReader mit dem richtigen Zeichensatz, lege ihn über denselben InputStream und versuche, den Text zu lesen.

Leider scheint es, javadoc bestätigt dies, dass InputStreamReader sich aus Effizienzgründen für das Vorauslesen entscheiden kann. Das Lesen des Headers kaut also einen Teil des Körpers.

Hat jemand Vorschläge zur Behebung dieses Problems? Würde es eine gute Idee sein, einen CharsetDecoder manuell zu erstellen und jeweils ein Byte einzugeben (möglicherweise in einer benutzerdefinierten Reader-Implementierung enthalten?)

Danke im Voraus

EDIT: Meine letzte Lösung bestand darin, einen InputStreamReader zu schreiben, der nicht gepuffert ist, um sicherzustellen, dass ich den Header analysieren kann, ohne einen Teil des Körpers zu kauen. Obwohl dies nicht besonders effizient ist, verpacke ich den rohen InputStream mit einem BufferedInputStream, damit dies kein Problem darstellt.

// An InputStreamReader that only consumes as many bytes as is necessary
// It does not do any read-ahead.
public class InputStreamReaderUnbuffered extends Reader
{
    private final CharsetDecoder charsetDecoder;
    private final InputStream inputStream;
    private final ByteBuffer byteBuffer = ByteBuffer.allocate( 1 );

    public InputStreamReaderUnbuffered( InputStream inputStream, Charset charset )
    {
        this.inputStream = inputStream;
        charsetDecoder = charset.newDecoder();
    }

    @Override
    public int read() throws IOException
    {
        boolean middleOfReading = false;

        while ( true )
        {
            int b = inputStream.read();

            if ( b == -1 )
            {
                if ( middleOfReading )
                    throw new IOException( "Unexpected end of stream, byte truncated" );

                return -1;
            }

            byteBuffer.clear();
            byteBuffer.put( (byte)b );
            byteBuffer.flip();

            CharBuffer charBuffer = charsetDecoder.decode( byteBuffer );

            // although this is theoretically possible this would violate the unbuffered nature
            // of this class so we throw an exception
            if ( charBuffer.length() > 1 )
                throw new IOException( "Decoded multiple characters from one byte!" );

            if ( charBuffer.length() == 1 )
                return charBuffer.get();

            middleOfReading = true;
        }
    }

    public int read( char[] cbuf, int off, int len ) throws IOException
    {
        for ( int i = 0; i < len; i++ )
        {
            int ch = read();

            if ( ch == -1 )
                return i == 0 ? -1 : i;

            cbuf[ i ] = (char)ch;
        }

        return len;
    }

    public void close() throws IOException
    {
        inputStream.close();
    }
}

Antworten auf die Frage(6)

Ihre Antwort auf die Frage