Я читаю данные из файла, который, к сожалению, имеет два типа кодировки символов.
Существует заголовок и тело. Заголовок всегда находится в ASCII и определяет набор символов, в котором тело закодировано.
Заголовок не является фиксированной длиной и должен проходить через анализатор для определения его содержимого/длины.
Файл также может быть довольно большим, поэтому мне нужно избегать переноса всего содержимого в память.
Итак, я начал с одного InputStream. Я сначала переношу его с помощью InputStreamReader с ASCII и декодирует заголовок и извлекаю набор символов для тела. Все хорошо.
Затем я создаю новый InputStreamReader с правильным набором символов, бросаю его за тот же InputStream и начинаю читать тело.
К сожалению, похоже, javadoc подтверждает это, что InputStreamReader может выбрать для чтения для целей эффективности. Таким образом, чтение заголовка жует какое-то/все тело.
Есть ли у кого-нибудь предложения по работе над этой проблемой? Будет ли создание CharsetDecoder вручную и подача в один байт за раз, но хорошая идея (возможно, завернутая в пользовательскую реализацию Reader?)
Спасибо заранее.
EDIT: Моим окончательным решением было написать 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();
}
}