web-dev-qa-db-fra.com

Comment puis-je obtenir un Java.io.InputStream à partir d'un Java.lang.String?

J'ai une String que je veux utiliser comme InputStream. En Java 1.0, vous pouvez utiliser Java.io.StringBufferInputStream , mais il s'agit de @Deprecrated (avec une bonne raison - vous ne pouvez pas spécifier le codage du jeu de caractères):

Cette classe ne convertit pas correctement caractères en octets. À partir de JDK 1.1, le moyen préféré pour créer un flux à partir d'une chaîne est via la StringReader classe.

Vous pouvez créer un Java.io.Reader avec Java.io.StringReader , mais aucun adaptateur ne permet de prendre une Reader et de créer une InputStream.

J'ai trouvé un ancien bug demandant un remplaçant adéquat, mais cela n'existe pas - pour autant que je sache.

La solution de contournement souvent suggérée consiste à utiliser Java.lang.String.getBytes() comme entrée dans Java.io.ByteArrayInputStream :

public InputStream createInputStream(String s, String charset)
    throws Java.io.UnsupportedEncodingException {

    return new ByteArrayInputStream(s.getBytes(charset));
}

mais cela signifie matérialiser l'intégralité de la String en mémoire sous la forme d'un tableau d'octets et vider le but d'un flux. Dans la plupart des cas, ce n'est pas grave, mais je cherchais quelque chose qui préserverait le sens d'un flux: le moins de données possible est (re) matérialisé en mémoire.

91
Jared Oberhaus

Mise à jour: Cette réponse est précisément ce que le PO ne veut pas. S'il vous plaît lire les autres réponses.

Pour les cas où nous ne nous soucions pas des données re-matérialisées en mémoire, veuillez utiliser:

new ByteArrayInputStream(str.getBytes("UTF-8"))
77
Andres Riofrio

Si une dépendance du paquet commons-io ne vous dérange pas, vous pouvez utiliser la méthode IOUtils.toInputStream (String text) .

18

Il existe un adaptateur d’Apache Commons-IO qui s’adapte de Reader à InputStream, nommé ReaderInputStream .

Exemple de code:

@Test
public void testReaderInputStream() throws IOException {
    InputStream inputStream = new ReaderInputStream(new StringReader("largeString"), StandardCharsets.UTF_8);
    Assert.assertEquals("largeString", IOUtils.toString(inputStream, StandardCharsets.UTF_8));
}

Référence: https://stackoverflow.com/a/27909221/5658642

3
beat

Pour moi, le moyen le plus simple de procéder consiste à transmettre les données à un enregistreur:

public class StringEmitter {
  public static void main(String[] args) throws IOException {
    class DataHandler extends OutputStream {
      @Override
      public void write(final int b) throws IOException {
        write(new byte[] { (byte) b });
      }
      @Override
      public void write(byte[] b) throws IOException {
        write(b, 0, b.length);
      }
      @Override
      public void write(byte[] b, int off, int len)
          throws IOException {
        System.out.println("bytecount=" + len);
      }
    }

    StringBuilder sample = new StringBuilder();
    while (sample.length() < 100 * 1000) {
      sample.append("sample");
    }

    Writer writer = new OutputStreamWriter(
        new DataHandler(), "UTF-16");
    writer.write(sample.toString());
    writer.close();
  }
}

L’implémentation de la machine virtuelle Java que j’utilise à partir de morceaux de 8 ko dans des données transmises par poussée.


Une alternative à l'écriture de votre propre wrapper CharsetEncoder pour utiliser un enregistreur pour encoder les données, bien que ce soit une tâche pénible à accomplir. Cela devrait être une implémentation fiable (si inefficace):

/** Inefficient string stream implementation */
public class StringInputStream extends InputStream {

  /* # of characters to buffer - must be >=2 to handle surrogate pairs */
  private static final int CHAR_CAP = 8;

  private final Queue<Byte> buffer = new LinkedList<Byte>();
  private final Writer encoder;
  private final String data;
  private int index;

  public StringInputStream(String sequence, Charset charset) {
    data = sequence;
    encoder = new OutputStreamWriter(
        new OutputStreamBuffer(), charset);
  }

  private int buffer() throws IOException {
    if (index >= data.length()) {
      return -1;
    }
    int rlen = index + CHAR_CAP;
    if (rlen > data.length()) {
      rlen = data.length();
    }
    for (; index < rlen; index++) {
      char ch = data.charAt(index);
      encoder.append(ch);
      // ensure data enters buffer
      encoder.flush();
    }
    if (index >= data.length()) {
      encoder.close();
    }
    return buffer.size();
  }

  @Override
  public int read() throws IOException {
    if (buffer.size() == 0) {
      int r = buffer();
      if (r == -1) {
        return -1;
      }
    }
    return 0xFF & buffer.remove();
  }

  private class OutputStreamBuffer extends OutputStream {

    @Override
    public void write(int i) throws IOException {
      byte b = (byte) i;
      buffer.add(b);
    }

  }

}
3
McDowell

Eh bien, un moyen possible est de:

  • Créer une PipedOutputStream
  • Dirigez-le vers un PipedInputStream
  • Enroulez une OutputStreamWriter autour de la PipedOutputStream (vous pouvez spécifier le codage dans le constructeur)
  • Et voila, tout ce que vous écrivez à OutputStreamWriter peut être lu à partir de PipedInputStream!

Bien sûr, cela semble être une façon plutôt maladroite de le faire, mais au moins c'est un moyen.

2
Michael Myers

Une solution consiste à créer votre propre implémentation, en créant une implémentation InputStream qui utiliserait probablement Java.nio.charset.CharsetEncoder pour coder chaque char ou bloc de chars dans un tableau d'octets pour InputStream si nécessaire.

2
Jared Oberhaus

Je sais que c’est une vieille question, mais j’avais moi-même le même problème aujourd’hui, et c’était ma solution:

public static InputStream getStream(final CharSequence charSequence) {
 return new InputStream() {
  int index = 0;
  int length = charSequence.length();
  @Override public int read() throws IOException {
   return index>=length ? -1 : charSequence.charAt(index++);
  }
 };
}
0
Paul Richards

Vous pouvez prendre l'aide de la bibliothèque org.hsqldb.lib.

public StringInputStream(String paramString)
  {
    this.str = paramString;
    this.available = (paramString.length() * 2);
  }
0
omar