Tôi đang đọc dữ liệu từ một tệp có, thật không may, hai loại mã hóa ký tự.InputStreamReader buffer issue
Có tiêu đề và nội dung. Tiêu đề luôn ở dạng ASCII và xác định bộ ký tự mà nội dung được mã hóa.
Tiêu đề không có độ dài cố định và phải được chạy qua trình phân tích cú pháp để xác định nội dung/độ dài của nó.
Tệp cũng có thể khá lớn nên tôi cần tránh đưa toàn bộ nội dung vào bộ nhớ.
Vì vậy, tôi bắt đầu với một InputStream duy nhất. Tôi quấn nó ban đầu với một InputStreamReader với ASCII và giải mã tiêu đề và trích xuất các ký tự đặt cho cơ thể. Tất cả đều tốt.
Sau đó, tôi tạo một InputStreamReader mới với bộ ký tự chính xác, thả nó trên cùng một InputStream và bắt đầu cố gắng đọc nội dung.
Thật không may là nó xuất hiện, javadoc xác nhận điều này, InputStreamReader có thể chọn đọc trước cho các mục đích hiệu quả. Vì vậy, việc đọc tiêu đề nhai một số/tất cả của cơ thể.
Có ai có bất kỳ đề xuất nào để giải quyết vấn đề này không? Có thể tạo CharsetDecoder theo cách thủ công và nạp vào một byte tại một thời điểm hay không nhưng ý tưởng hay (có thể được bao bọc trong triển khai Reader tùy chỉnh?)
Xin cảm ơn trước.
EDIT: Giải pháp cuối cùng của tôi là viết InputStreamReader không có bộ đệm để đảm bảo tôi có thể phân tích cú pháp tiêu đề mà không cần nhai một phần cơ thể. Mặc dù đây không phải là một cách hiệu quả khủng khiếp nhưng tôi đưa InputStream nguyên bản vào một BufferedInputStream để nó không phải là một vấn đề.
// 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();
}
}
Có lẽ tôi là sai, nhưng kể từ thời điểm này tôi nghĩ rằng tập tin có thể chỉ có một loại mã hóa cùng một lúc. – Roman
@Roman: Bạn có thể làm bất cứ điều gì bạn muốn với các tập tin; chúng chỉ là chuỗi các byte. Vì vậy, bạn có thể viết ra một loạt các byte được hiểu là ASCII, sau đó viết ra một bó nhiều byte hơn được hiểu là UTF-16 và thậm chí nhiều byte hơn được hiểu là UTF-32. Tôi không nói đó là một ý tưởng tốt, mặc dù trường hợp sử dụng của OP là chắc chắn hợp lý (bạn phải có * một số * cách chỉ ra những gì mã hóa một tập tin sử dụng, sau khi tất cả). –
@Mike Q - Ý tưởng tốt là InputStreamReaderUnbuffered. Tôi đề nghị một câu trả lời riêng biệt - nó xứng đáng được chú ý :) –