java - array - inputstream to file
¿Cómo miro los primeros dos bytes en un InputStream? (4)
Debería ser bastante simple: tengo un InputStream en el que quiero ver (no leer) los dos primeros bytes, es decir, quiero que la "posición actual" del InputStream permanezca en 0 después de mi inspección. ¿Cuál es la mejor y más segura forma de hacer esto?
Respuesta : como había sospechado, la solución era envolverlo en un BufferedInputStream que ofrezca marcabilidad. Gracias Rasmus.
Cuando se utiliza un BufferedInputStream, asegúrese de que inputStream no esté almacenado en el búfer, el doble búfer causará errores muy difíciles de encontrar. También necesita manejar los Lectores de manera diferente, convirtiendo a un StreamReader y el Buffering hará que los bytes se pierdan si el Lector está en Tampón. Además, si usa un lector, recuerde que no está leyendo bytes, sino caracteres en la codificación predeterminada (a menos que se haya establecido una codificación explícita). Un ejemplo de una corriente de entrada almacenada en el búfer, que puede que no sepa, es URL url; url.openStream ();
No tengo referencias para esta información, proviene del código de depuración. El caso principal en el que se produjo el problema fue el código que se lee de un archivo en una secuencia comprimida. Si recuerdo correctamente una vez que comience la depuración a través del código, hay comentarios en la fuente Java de que ciertas cosas no funcionan correctamente siempre. No recuerdo de dónde viene la información del uso de BufferedReader y BufferedInputStream, pero creo que falla de inmediato incluso en la prueba más simple. Recuerde probar que necesita marcar más que el tamaño del búfer (que es diferente para BufferedReader versus BufferedInputStream), los problemas ocurren cuando los bytes que se leen llegan al final del búfer. Tenga en cuenta que hay un tamaño de búfer de código fuente que puede ser diferente al tamaño de búfer que estableció en el constructor. Ha pasado un tiempo desde que hice esto, así que mis recuerdos de detalles pueden estar un poco fuera de lugar. Las pruebas se realizaron utilizando un FilterReader / FilterInputStream, agregué uno a la transmisión directa y uno a la transmisión almacenada para ver la diferencia.
Encontré una implementación de PeekableInputStream aquí:
http://www.heatonresearch.com/articles/147/page2.html
La idea de la implementación que se muestra en el artículo es que mantiene una matriz de valores "ocultos" internamente. Cuando llamas a read, los valores se devuelven primero desde la matriz peeked, luego desde la corriente de entrada. Cuando llamas a peek, los valores se leen y almacenan en la matriz "peeked".
Como la licencia del código de muestra es LGPL, se puede adjuntar a esta publicación:
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;
}
}
Para un InputStream general, lo envolvería en un BufferedInputStream y haré algo como esto:
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
Es posible que PushbackInputStream sea útil:
http://docs.oracle.com/javase/6/docs/api/java/io/PushbackInputStream.html