Pregunta

¿Hay alguna manera de leer un ByteBuffer con un BufferedReader sin tener que convertirlo primero en una Cadena?Quiero leer un ByteBuffer bastante grande como líneas de texto y, por razones de rendimiento, quiero evitar escribirlo en el disco.Llamar a toString en ByteBuffer no funciona porque la cadena resultante es demasiado grande (arroja java.lang.OutOfMemoryError:Espacio de almacenamiento dinámico de Java).Pensé que habría algo en la API para incluir un ByteBuffer en un lector adecuado, pero parece que no puedo encontrar nada adecuado.

Aquí hay un ejemplo de código abreviado que ilustra lo que estoy haciendo):

// input stream is from Process getInputStream()
public String read(InputStream istream)
{
  ReadableByteChannel source = Channels.newChannel(istream);
  ByteArrayOutputStream ostream = new ByteArrayOutputStream(bufferSize);
  WritableByteChannel destination = Channels.newChannel(ostream);
  ByteBuffer buffer = ByteBuffer.allocateDirect(writeBufferSize);

  while (source.read(buffer) != -1)
  {
    buffer.flip();
    while (buffer.hasRemaining())
    {
      destination.write(buffer);
    }
    buffer.clear();
  }

  // this data can be up to 150 MB.. won't fit in a String.
  result = ostream.toString();
  source.close();
  destination.close();
  return result;
}

// after the process is run, we call this method with the String
public void readLines(String text)
{
  BufferedReader reader = new BufferedReader(new StringReader(text));
  String line;

  while ((line = reader.readLine()) != null)
  {
    // do stuff with line
  }
}
¿Fue útil?

Solución

Para empezar, no está claro por qué estás utilizando un búfer de bytes.Si tienes un InputStream y quieres leer líneas, ¿por qué no usas simplemente un InputStreamReader envuelto en un BufferedReader?¿Cuál es el beneficio de involucrar a NIO?

Vocación toString() en un ByteArrayOutputStream Me parece una mala idea incluso si tuvieras espacio para ello:mejor obtenerlo como una matriz de bytes y envolverlo en un ByteArrayInputStream y luego un InputStreamReader, si realmente tienes que tener un ByteArrayOutputStream.Si usted en realidad quiero llamar toString(), al menos use la sobrecarga que toma el nombre de la codificación de caracteres a usar; de lo contrario, usará el valor predeterminado del sistema, que probablemente no sea lo que desea.

EDITAR:Bien, entonces realmente quieres usar NIO.Todavía estás escribiendo a un ByteArrayOutputStream eventualmente, terminará con un BAOS con los datos que contiene.Si desea evitar hacer una copia de esos datos, deberá derivar de ByteArrayOutputStream, por ejemplo así:

public class ReadableByteArrayOutputStream extends ByteArrayOutputStream
{
    /**
     * Converts the data in the current stream into a ByteArrayInputStream.
     * The resulting stream wraps the existing byte array directly;
     * further writes to this output stream will result in unpredictable
     * behavior.
     */
    public InputStream toInputStream()
    {
        return new ByteArrayInputStream(array, 0, count);
    }
}

Luego puede crear el flujo de entrada, envolverlo en un InputStreamReader, envuélvelo en un BufferedReader, y estás lejos.

Otros consejos

Puede usar NIO, pero no hay una necesidad real aquí. Como Jon Skeet sugirió:

public byte[] read(InputStream istream)
{
  ByteArrayOutputStream baos = new ByteArrayOutputStream();
  byte[] buffer = new byte[1024]; // Experiment with this value
  int bytesRead;

  while ((bytesRead = istream.read(buffer)) != -1)
  {
    baos.write(buffer, 0, bytesRead);
  }

  return baos.toByteArray();
}


// after the process is run, we call this method with the String
public void readLines(byte[] data)
{
  BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(data)));
  String line;

  while ((line = reader.readLine()) != null)
  {
    // do stuff with line
  }
}

Esta es una muestra:

public class ByteBufferBackedInputStream extends InputStream {

    ByteBuffer buf;

    public ByteBufferBackedInputStream(ByteBuffer buf) {
        this.buf = buf;
    }

    public synchronized int read() throws IOException {
        if (!buf.hasRemaining()) {
            return -1;
        }
        return buf.get() & 0xFF;
    }

    @Override
    public int available() throws IOException {
        return buf.remaining();
    }

    public synchronized int read(byte[] bytes, int off, int len) throws IOException {
        if (!buf.hasRemaining()) {
            return -1;
        }

        len = Math.min(len, buf.remaining());
        buf.get(bytes, off, len);
        return len;
    }
}

Y puedes usarlo así:

    String text = "this is text";   // It can be Unicode text
    ByteBuffer buffer = ByteBuffer.wrap(text.getBytes("UTF-8"));

    InputStream is = new ByteBufferBackedInputStream(buffer);
    InputStreamReader r = new InputStreamReader(is, "UTF-8");
    BufferedReader br = new BufferedReader(r);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top