Как заглянуть в первые два байта в InputStream?
Должно быть довольно просто: у меня есть InputStream, где я хочу заглядывать (не читать) первые два байта, т.е. я хочу, чтобы "текущая позиция" InputStream была равна 0 после моего подглядывания. Каков наилучший и безопасный способ сделать это?
Ответ. Как я и подозревал, решение заключалось в том, чтобы обернуть его в BufferedInputStream, который предлагает возможность маркировки. Спасибо Расмусу.
Ответы
Ответ 1
Для общего InputStream я бы обернул его в BufferedInputStream и сделаю что-то вроде этого:
BufferedInputStream bis = new BufferedInputStream(inputStream);
bis.mark(2);
int byte1 = bis.read();
int byte2 = bis.read();
bis.reset();
// note: you must continue using the BufferedInputStream instead of the inputStream
Ответ 2
Вы можете найти PushbackInputStream, чтобы быть полезным:
http://docs.oracle.com/javase/6/docs/api/java/io/PushbackInputStream.html
Ответ 3
При использовании BufferedInputStream убедитесь, что inputStream уже не буферизирован, двойная буферизация приведет к серьезным затруднениям поиска ошибок.
Также вам нужно обрабатывать читателей по-разному, преобразование в StreamReader и буферизация приведет к потере байтов, если буферизация Reader.
Также, если вы используете Reader, помните, что вы не читаете байты, а символы в кодировке по умолчанию (если только не была установлена явная кодировка).
Примером буферизованного входного потока, который вы, возможно, не знаете, является URL-адрес URL; url.openStream();
У меня нет ссылок на эту информацию, она исходит из кода отладки.
Основной случай, когда проблема возникла для меня, - это код, который считывается из файла в сжатый поток.
Если я правильно помню, как только вы начинаете отладку через код, в источнике Java есть комментарии, что некоторые вещи работают неправильно.
Я не помню, где информация об использовании BufferedReader и BufferedInputStream
приходит, но я думаю, что это не удается сразу даже на самом простом испытании.
Не забудьте протестировать это, вам нужно маркировать больше, чем размер буфера (который отличается от BufferedReader и BufferedInputStream), проблемы возникают, когда прочитанные байты достигают конца буфера.
Обратите внимание, что существует размер буфера исходного кода, который может отличаться от размера буфера, установленного в конструкторе.
Прошло некоторое время с тех пор, как я сделал это, поэтому мои воспоминания о деталях могут быть немного разочарованы.
Тестирование было выполнено с использованием фильтра FilterReader/FilterInputStream, добавьте его в прямой поток и один в буферный поток, чтобы увидеть разницу.
Ответ 4
Я нашел реализацию PeekableInputStream здесь:
http://www.heatonresearch.com/articles/147/page2.html
Идея реализации, показанная в статье, заключается в том, что она хранит массив значений "peeked" внутри. Когда вы вызываете read, значения возвращаются сначала из peeked массива, а затем из входного потока. Когда вы вызываете peek, значения считываются и сохраняются в массиве "peeked".
Как лицензия образца кода LGPL, он может быть прикреплен к этому сообщению:
package com.heatonresearch.httprecipes.html;
import java.io.*;
/**
* The Heaton Research Spider Copyright 2007 by Heaton
* Research, Inc.
*
* HTTP Programming Recipes for Java ISBN: 0-9773206-6-9
* http://www.heatonresearch.com/articles/series/16/
*
* PeekableInputStream: This is a special input stream that
* allows the program to peek one or more characters ahead
* in the file.
*
* This class is released under the:
* GNU Lesser General Public License (LGPL)
* http://www.gnu.org/copyleft/lesser.html
*
* @author Jeff Heaton
* @version 1.1
*/
public class PeekableInputStream extends InputStream
{
/**
* The underlying stream.
*/
private InputStream stream;
/**
* Bytes that have been peeked at.
*/
private byte peekBytes[];
/**
* How many bytes have been peeked at.
*/
private int peekLength;
/**
* The constructor accepts an InputStream to setup the
* object.
*
* @param is
* The InputStream to parse.
*/
public PeekableInputStream(InputStream is)
{
this.stream = is;
this.peekBytes = new byte[10];
this.peekLength = 0;
}
/**
* Peek at the next character from the stream.
*
* @return The next character.
* @throws IOException
* If an I/O exception occurs.
*/
public int peek() throws IOException
{
return peek(0);
}
/**
* Peek at a specified depth.
*
* @param depth
* The depth to check.
* @return The character peeked at.
* @throws IOException
* If an I/O exception occurs.
*/
public int peek(int depth) throws IOException
{
// does the size of the peek buffer need to be extended?
if (this.peekBytes.length <= depth)
{
byte temp[] = new byte[depth + 10];
for (int i = 0; i < this.peekBytes.length; i++)
{
temp[i] = this.peekBytes[i];
}
this.peekBytes = temp;
}
// does more data need to be read?
if (depth >= this.peekLength)
{
int offset = this.peekLength;
int length = (depth - this.peekLength) + 1;
int lengthRead = this.stream.read(this.peekBytes, offset, length);
if (lengthRead == -1)
{
return -1;
}
this.peekLength = depth + 1;
}
return this.peekBytes[depth];
}
/*
* Read a single byte from the stream. @throws IOException
* If an I/O exception occurs. @return The character that
* was read from the stream.
*/
@Override
public int read() throws IOException
{
if (this.peekLength == 0)
{
return this.stream.read();
}
int result = this.peekBytes[0];
this.peekLength--;
for (int i = 0; i < this.peekLength; i++)
{
this.peekBytes[i] = this.peekBytes[i + 1];
}
return result;
}
}