Как просмотреть первые два байта входного потока?
-
02-07-2019 - |
Вопрос
Должно быть довольно просто:У меня есть InputStream, где я хочу просмотреть (не прочитать) первые два байта, т.е.Я хочу, чтобы после моего просмотра «текущая позиция» InputStream по-прежнему была равна 0.Каков наилучший и безопасный способ сделать это?
Отвечать - Как я и подозревал, решение заключалось в том, чтобы обернуть его в BufferedInputStream, который обеспечивает возможность маркировки.Спасибо, Расмус.
Решение
Для общего 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
Другие советы
Вы можете найти PushbackInputStream полезным:
http://docs.oracle.com/javase/6/docs/api/java/io/PushbackInputStream.html
При использовании BufferedInputStream убедитесь, что входной поток еще не буферизован, двойная буферизация приведет к серьезному обнаружению ошибок.Кроме того, вам необходимо по-другому обрабатывать устройства чтения: преобразование в StreamReader и буферизация приведет к потере байтов, если устройство чтения находится в буферизации.Также, если вы используете Reader, помните, что вы читаете не байты, а символы в кодировке по умолчанию (если не была установлена явная кодировка).Пример буферизованного входного потока, о котором вы, возможно, не знаете, — это URL-адрес;URL.openStream();
У меня нет ссылок на эту информацию, она взята из кода отладки.Основной случай, когда проблема возникла у меня, был в коде, который читал из файла в сжатый поток.Если я правильно помню, как только вы начнете отлаживать код, в исходном коде Java будут комментарии о том, что некоторые вещи не всегда работают правильно.Я не помню, откуда берется информация от использования BufferedReader и BufferedInptream, но я думаю, что это сразу не сразу же при простейшем тесте.Не забудьте проверить это, вам нужно пометить размер, превышающий размер буфера (который отличается для BufferedReader и BufferedInputStream), проблемы возникают, когда читаемые байты достигают конца буфера.Обратите внимание, что размер буфера исходного кода может отличаться от размера буфера, установленного вами в конструкторе.Прошло много времени с тех пор, как я это делал, поэтому мои воспоминания о деталях могут быть немного ошибочными.Тестирование проводилось с использованием FilterReader/FilterInputStream: добавьте один в прямой поток и один в буферизованный поток, чтобы увидеть разницу.
Здесь я нашел реализацию PeekableInputStream:
http://www.heatonresearch.com/articles/147/page2.html
Идея реализации, показанной в статье, заключается в том, что она хранит внутри себя массив «просмотренных» значений.Когда вы вызываете read, значения возвращаются сначала из просмотренного массива, а затем из входного потока.Когда вы вызываете peek, значения считываются и сохраняются в «просматриваемом» массиве.
Поскольку лицензия примера кода — 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;
}
}