Question

Existe-t-il un moyen de lire un ByteBuffer avec BufferedReader sans avoir à le transformer en chaîne au préalable? Je veux lire un ByteBuffer assez volumineux sous forme de lignes de texte et pour des raisons de performances, je veux éviter de l'écrire sur le disque. L'appel de toString sur ByteBuffer ne fonctionne pas car la chaîne résultante est trop volumineuse (elle renvoie java.lang.OutOfMemoryError: espace de segment de mémoire Java). J'aurais pensé qu'il y aurait quelque chose dans l'API pour insérer un ByteBuffer dans un lecteur approprié, mais je n'arrive pas à trouver quelque chose qui convienne.

Voici un exemple de code abrégé qui illustre ce que je fais):

// 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
  }
}
Était-ce utile?

La solution

La raison pour laquelle vous utilisez un tampon d’octets n’est pas claire. Si vous avez un InputStream et que vous voulez lire des lignes, pourquoi ne pas simplement utiliser un InputStreamReader entouré d'un BufferedReader? Quel est l'intérêt de faire participer NIO?

Appeler toString() sur un ByteArrayOutputStream me semble une mauvaise idée même si vous en aviez assez de place: mieux vaut l’obtenir sous forme de tableau d’octets et l’envelopper dans un ByteArrayInputStream puis un <=> , si vous devez vraiment avoir un <=>. Si vous voulez vraiment appeler <=>, utilisez au moins la surcharge qui prend le nom du codage de caractères à utiliser - sinon, la valeur par défaut du système sera utilisée, ce qui n'est probablement pas ce que vous voulez. .

EDIT: Bon, vous voulez vraiment utiliser NIO. Vous écrivez toujours sur un <=>, si bien que vous obtiendrez un BAOS contenant les données qu'il contient. Si vous souhaitez éviter de copier ces données, vous devez dériver de <=>, par exemple, comme suit:

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

Ensuite, vous pouvez créer le flux d'entrée, le placer dans un <=>, le placer dans un <=>, et vous êtes absent.

Autres conseils

Vous pouvez utiliser NIO, mais ce n’est pas vraiment utile ici. Comme Jon Skeet l'a suggéré:

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

Ceci est un exemple:

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

Et vous pouvez l'utiliser comme ceci:

    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);
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top