BufferedReader для большого ByteBuffer? - PullRequest
3 голосов
/ 25 июня 2009

Есть ли способ прочитать ByteBuffer с BufferedReader без необходимости сначала превращать его в строку? Я хочу прочитать довольно большой ByteBuffer в виде строк текста, и по соображениям производительности я хочу избежать записи его на диск. Вызов toString для ByteBuffer не работает, потому что результирующая строка слишком велика (она выбрасывает java.lang.OutOfMemoryError: пространство кучи Java). Я бы подумал, что в API будет что-то, чтобы обернуть ByteBuffer в подходящий ридер, но я не могу найти ничего подходящего.

Вот сокращенный пример кода, иллюстрирующий то, что я делаю):

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

Ответы [ 3 ]

5 голосов
/ 25 июня 2009

Непонятно, для чего вы используете байтовый буфер для начала. Если у вас есть InputStream и вы хотите прочитать строки для него, почему бы вам просто не использовать InputStreamReader, завернутый в BufferedReader? Какая польза от участия NIO?

Вызов toString() для ByteArrayOutputStream звучит для меня плохой идеей, даже если у вас есть место для него: лучше взять его в виде байтового массива и обернуть в ByteArrayInputStream, а затем InputStreamReader , если вам действительно нужно иметь ByteArrayOutputStream. Если вы действительно хотите вызвать toString(), хотя бы используйте перегрузку, которая принимает имя используемой кодировки символов - в противном случае будет использоваться системное значение по умолчанию, что, вероятно, не то, что вам нужно.

РЕДАКТИРОВАТЬ: Хорошо, так что вы действительно хотите использовать NIO. В конце концов, вы все еще пишете в ByteArrayOutputStream, поэтому вы получите BAOS с данными в нем. Если вы хотите избежать копирования этих данных, вам нужно извлечь из ByteArrayOutputStream, например, вот так:

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

Затем вы можете создать входной поток, обернуть его в InputStreamReader, обернуть его в BufferedReader, и вы в отъезде.

4 голосов
/ 25 июня 2009

Вы можете использовать NIO, но здесь нет особой необходимости. Как предложил Джон Скит:

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
  }
}
0 голосов
/ 11 ноября 2012

Это образец:

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

И вы можете использовать его так:

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