Pergunta

Existe uma maneira de ler um ByteBuffer com um BufferedReader sem ter que transformá-lo em um String em primeiro lugar? Eu quero ler através de um bastante grande ByteBuffer como linhas de texto e por motivos de desempenho Eu quero evitar escrevê-lo para o disco. Chamando toString na ByteBuffer não funciona porque a cadeia resultante é muito grande (ele lança java.lang.OutOfMemoryError: Java heap space). Eu teria pensado que haveria algo na API para embrulhar um ByteBuffer em um leitor apropriado, mas eu não consigo encontrar nada adequado.

Aqui está um exemplo de código abreviado o ilustra o que estou fazendo):

// 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
  }
}
Foi útil?

Solução

Não é claro por que você está usando um buffer de byte para começar. Se você tem um InputStream e você quiser ler as linhas para isso, por que você não apenas usar um InputStreamReader envolto em um BufferedReader? Qual é a vantagem na obtenção de NIO envolvido?

Chamando toString() em um sons ByteArrayOutputStream como uma má idéia para mim, mesmo se você tivesse o espaço para ele: melhor para obtê-lo como um array de bytes e envolvê-la em um ByteArrayInputStream e, em seguida, um InputStreamReader, se você realmente tem que ter um ByteArrayOutputStream. Se você realmente quiser chamar toString(), pelo menos, usar a sobrecarga que leva o nome da codificação de caracteres para uso - caso contrário, ele vai usar o padrão do sistema, que provavelmente não é o que você quer <. / p>

EDIT: Ok, então você realmente quer usar NIO. Você ainda está escrevendo a um ByteArrayOutputStream, eventualmente, então você vai acabar com um BAOS com os dados nele. Se você quiser evitar fazer uma cópia desses dados, você precisará derivar ByteArrayOutputStream, por exemplo como este:

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);
    }
}

Em seguida, você pode criar o fluxo de entrada, envolvê-la em um InputStreamReader, envoltório que, em um BufferedReader, e você estiver ausente.

Outras dicas

Você pode usar NIO, mas não há nenhuma necessidade real aqui. Como Jon Skeet sugerido:

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
  }
}

Este é um exemplo:

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;
    }
}

E você pode usá-lo como este:

    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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top