Frage

Gibt es eine Möglichkeit, eine ByteBuffer mit einem BufferedReader zu lesen, ohne es in einen String zuerst zu drehen? Ich möchte durch einen ziemlich großen ByteBuffer als Textzeilen und aus Leistungsgründen lesen ich es auf die Festplatte zu vermeiden, will zu schreiben. Der Aufruf toString auf dem ByteBuffer nicht funktioniert, weil der resultierende String zu groß ist (es wirft java.lang.OutOfMemoryError: Java Heap-Speicher). Ich hätte gedacht, es etwas in der API wäre eine ByteBuffer in einem geeigneten Leser zu wickeln, aber ich kann nicht scheinen, etwas Passendes zu finden.

Hier ist ein verkürzte Codebeispiel das illustriert, was ich tue):

// 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
  }
}
War es hilfreich?

Lösung

Es ist nicht klar, warum Sie einen Byte-Puffer verwenden zu beginnen. Wenn Sie eine InputStream haben und möchten, dass Sie Linien für ihn lesen, warum Sie nicht einfach eine InputStreamReader in einem BufferedReader gewickelt verwenden? Was ist der Nutzen in immer NIO beteiligt?

Beim toString() auf einem ByteArrayOutputStream klingt wie eine schlechte Idee zu mir, auch wenn Sie den Raum für sie hatte: besser als ein Byte-Array zu erhalten und es in einem ByteArrayInputStream wickeln und dann eine InputStreamReader, wenn Sie wirklich eine haben müssen ByteArrayOutputStream. Wenn Sie wirklich will toString() nennen, zumindest die Überlastung benutzen, die den Namen der Zeichencodierung nimmt zu verwenden -. Sonst wird es das System standardmäßig verwenden, das ist wahrscheinlich nicht das, was Sie wollen

EDIT: Okay, so dass Sie wirklich NIO verwenden möchten. Sie schreiben immer noch zu einem ByteArrayOutputStream schließlich, so dass Sie mit einem BAOS mit den Daten in es am Ende. Wenn Sie vermeiden möchten, eine Kopie dieser Daten machen, müssen Sie von ByteArrayOutputStream, beispielsweise wie folgt abzuleiten:

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

Dann können Sie den Eingangsstrom erzeugen, wickeln Sie es in einer InputStreamReader, wickeln, dass in einem BufferedReader, und du bist weg.

Andere Tipps

Sie können NIO verwenden, aber es gibt keine wirkliche Notwendigkeit hier. Als Jon Skeet vorgeschlagen:

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

Dies ist ein Beispiel:

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

Und Sie können es wie folgt verwendet werden:

    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);
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top