残念ながら、2 種類の文字エンコーディングを持つファイルからデータを読み取っています。
ヘッダーとボディがあります。ヘッダーは常に ASCII であり、本文がエンコードされる文字セットを定義します。
ヘッダーは固定長ではなく、その内容/長さを判別するためにパーサーを実行する必要があります。
ファイルも非常に大きくなる可能性があるため、コンテンツ全体をメモリに取り込まないようにする必要があります。
そこで、単一の InputStream から始めました。最初に ASCII の InputStreamReader でラップし、ヘッダーをデコードして本文の文字セットを抽出します。すべて良い。
次に、正しい文字セットを使用して新しい InputStreamReader を作成し、それを同じ InputStream にドロップして、本文の読み取りを試みます。
残念ながら、javadoc はこれを確認しており、InputStreamReader が効率的な目的で先読みを選択する可能性があります。したがって、ヘッダーの読み取りは、本文の一部/すべてを噛み砕きます。
この問題を回避するための提案はありますか? 手動で CharsetDecoder を作成し、一度に 1 バイトずつフィードするのは良い考えでしょうか (おそらくカスタムの Reader 実装にラップされていますか?)
前もって感謝します。
編集:私の最終的な解決策は、バッファリングのない InputStreamReader を記述して、本文の一部を噛むことなくヘッダーを解析できるようにすることでした。これは非常に効率的ではありませんが、生の InputStream を BufferedInputStream でラップするので、問題にはなりません。
// 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();
}
}