¿Cómo puedo obtener un java.io.InputStream de un java.lang.String?

95

Tengo un Stringque quiero usar como InputStream. En Java 1.0, podría usar java.io.StringBufferInputStream, pero así ha sido @Deprecrated(con una buena razón, no puede especificar la codificación del juego de caracteres):

Esta clase no convierte correctamente los caracteres en bytes. A partir de JDK 1.1, la forma preferida de crear una secuencia a partir de una cadena es a través de la StringReader clase.

Puede crear un java.io.Readercon java.io.StringReader, pero no hay adaptadores para tomar un Readery crear un InputStream.

Encontré un error antiguo que solicitaba un reemplazo adecuado, pero no existe tal cosa, por lo que yo sé.

La solución alternativa sugerida con frecuencia es utilizarla java.lang.String.getBytes()como entrada para java.io.ByteArrayInputStream:

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

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

pero eso significa materializar todo Stringen la memoria como una matriz de bytes, y anula el propósito de una secuencia. En la mayoría de los casos, esto no es gran cosa, pero estaba buscando algo que preservara la intención de un flujo: que la menor cantidad de datos posible se (re) materialice en la memoria.

Jared Oberhaus
fuente

Respuestas:

78

Actualización: esta respuesta es precisamente lo que el OP no quiere. Lea las otras respuestas.

Para aquellos casos en los que no nos importa que los datos se vuelvan a materializar en la memoria, utilice:

new ByteArrayInputStream(str.getBytes("UTF-8"))
Andrés Riofrío
fuente
3
La solución propuesta por esta respuesta ha sido anticipada, contemplada y rechazada por la pregunta. Entonces, en mi opinión, esta respuesta debería eliminarse.
Mike Nakis
1
Puede que tengas razón. Originalmente hice un comentario probablemente porque no era una respuesta real a la pregunta de OP.
Andrés Riofrío
28
Como visitante que viene aquí debido al título de la pregunta, me alegra que esta respuesta esté aquí. Entonces: no elimine esta respuesta. El comentario en la parte superior "Esta respuesta es precisamente lo que el PO no quiere. Lea las otras respuestas". es suficiente.
Yaakov Belch
10
A partir de java7:new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8))
lento
19

Si no le importa una dependencia del paquete commons-io , entonces puede usar el método IOUtils.toInputStream (String text) .

Fotis Paraskevopoulos
fuente
11
En ese caso, agrega una dependencia que no hace nada más que `return new ByteArrayInputStream (input.getBytes ()); ' ¿Realmente vale la pena una dependencia? Honestamente, no, no lo es.
whaefelinger
3
Es cierto, además, es exactamente la solución que el operador no quiere usar porque no quiere "materializar la cadena en la memoria" en oposición a que la cadena se materialice en otro lugar del sistema :)
Fotis Paraskevopoulos
¿Tenemos alguna biblioteca que convierta un objeto personalizado en una fuente de flujo de entrada? algo como IOUtils.toInputStream (objeto MyObject)?
nawazish-stackoverflow
5

Hay un adaptador de Apache Commons-IO que se adapta de Reader a InputStream, que se llama ReaderInputStream .

Código de ejemplo:

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

Referencia: https://stackoverflow.com/a/27909221/5658642

golpear
fuente
3

En mi opinión, la forma más sencilla de hacerlo es enviando los datos a través de un editor:

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

La implementación de JVM que estoy usando empujó datos en fragmentos de 8K, pero podría tener algún efecto en el tamaño del búfer al reducir la cantidad de caracteres escritos a la vez y llamar a flush.


Una alternativa a escribir su propio contenedor CharsetEncoder para usar un Writer para codificar los datos, aunque es algo complicado hacerlo bien. Esta debería ser una implementación confiable (aunque ineficiente):

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

  }

}
McDowell
fuente
2

Bueno, una forma posible es:

  • Crear un PipedOutputStream
  • Típelo a un PipedInputStream
  • Envuelva un OutputStreamWriteralrededor de PipedOutputStream(puede especificar la codificación en el constructor)
  • Et voilá, cualquier cosa que le escriba OutputStreamWriterse puede leer desde el PipedInputStream!

Por supuesto, esta parece una forma bastante hackea de hacerlo, pero al menos es una forma.

Michael Myers
fuente
1
Interesante ... por supuesto, con esta solución creo que materializarías toda la cadena en la memoria, o sufrirías hambre en el hilo de lectura. Todavía espero que haya una implementación real en alguna parte.
Jared Oberhaus
5
Tienes que tener cuidado con la corriente canalizada (entrada | salida). Según los documentos: "... No se recomienda intentar usar ambos objetos de un solo hilo, ya que puede bloquear el hilo ..." java.sun.com/j2se/1.4.2/docs/api/java/ io / PipedInputStream.html
Bryan Kyle
1

Una solución es lanzar la suya propia, creando una InputStreamimplementación que probablemente se usaría java.nio.charset.CharsetEncoderpara codificar cada uno charo un fragmento de chars en una matriz de bytes InputStreamsegún sea necesario.

Jared Oberhaus
fuente
1
Hacer las cosas de un personaje a la vez es caro. Es por eso que tenemos "iteradores fragmentados" como InputStream que nos permiten leer un búfer a la vez.
Tom Hawtin - tackline
Estoy de acuerdo con Tom: realmente no quieres hacer este personaje a la vez.
Eddie
1
A menos que los datos sean realmente pequeños y otras cosas (la latencia de la red, por ejemplo) tarden más. Entonces no importa. :)
Andres Riofrio
0

Puede tomar la ayuda de la biblioteca org.hsqldb.lib.

public StringInputStream(String paramString)
  {
    this.str = paramString;
    this.available = (paramString.length() * 2);
  }
omar
fuente
1
Generalmente, las preguntas son mucho más útiles si incluyen una explicación de lo que se pretende que haga el código.
Peter
-1

Sé que esta es una pregunta antigua, pero hoy tuve el mismo problema y esta fue mi solución:

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++);
  }
 };
}
Paul Richards
fuente