Как посмотреть первые два байта в InputStream? - PullRequest
29 голосов
/ 29 сентября 2008

Должно быть довольно просто: у меня есть InputStream, где я хочу посмотреть (не прочитать) первые два байта, т. Е. Я хочу, чтобы «текущая позиция» InputStream все еще была в 0 после моего просмотра. Какой самый лучший и безопасный способ сделать это?

Ответ - Как я и подозревал, решение было заключить его в BufferedInputStream, который предлагает маркируемость. Спасибо Расмус.

Ответы [ 4 ]

49 голосов
/ 29 сентября 2008

Для общего 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
17 голосов
/ 29 сентября 2008

Может оказаться полезным PushbackInputStream:

http://docs.oracle.com/javase/6/docs/api/java/io/PushbackInputStream.html

5 голосов
/ 29 сентября 2008

При использовании BufferedInputStream убедитесь, что inputStream еще не буферизован, двойная буферизация вызовет некоторые серьезные проблемы с обнаружением ошибок. Также вам нужно по-разному обрабатывать Reader, преобразование в StreamReader и буферизацию приведет к потере байтов, если Reader будет буферизован. Также, если вы используете Reader, помните, что вы читаете не байты, а символы в кодировке по умолчанию (если не была задана явная кодировка). Пример буферизованного входного потока, который вы, возможно, не знаете, - URL-адрес URL; url.openStream (); * * тысяча одна

У меня нет ссылок на эту информацию, это происходит из кода отладки. Основной случай, когда проблема возникла для меня, был в коде, который читал из файла в сжатый поток. Если я правильно помню, как только вы начнете отлаживать код, в исходном коде Java есть комментарии о том, что некоторые вещи работают не всегда корректно. Я не помню, откуда информация от использования BufferedReader и BufferedInputStream исходит из, но я думаю, что это не сразу, даже на самом простом тесте. Не забудьте проверить это, вам нужно пометить больше, чем размер буфера (который отличается для BufferedReader и BufferedInputStream), проблемы возникают, когда считываемые байты достигают конца буфера. Обратите внимание, что есть размер буфера исходного кода, который может отличаться от размера буфера, который вы установили в конструкторе. Прошло некоторое время с тех пор, как я это сделал, поэтому мои воспоминания о деталях могут быть немного не в порядке. Тестирование было выполнено с использованием FilterReader / FilterInputStream, добавьте один в прямой поток и один в буферный поток, чтобы увидеть разницу.

4 голосов
/ 29 сентября 2008

Я нашел реализацию PeekableInputStream здесь:

http://www.heatonresearch.com/articles/147/page2.html

Идея реализации, показанной в статье, заключается в том, что она хранит массив «заглядывающих» значений внутри. Когда вы вызываете read, значения возвращаются сначала из проверенного массива, а затем из входного потока. Когда вы вызываете 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;
  }

}
...