2009-05-08 4 views
83

Tengo un String que quiero usar como InputStream. En Java 1.0, puede utilizar java.io.StringBufferInputStream, pero que ha sido @Deprecrated (con buena razón - no se puede especificar el juego de codificación de caracteres):¿Cómo puedo obtener un java.io.InputStream desde un java.lang.String?

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

Se puede crear un java.io.Reader con java.io.StringReader, pero no hay adaptadores para tomar un Reader y crear un InputStream.

Encontré un ancient bug pidiendo un reemplazo adecuado, pero no existe tal cosa, por lo que puedo decir.

La solución sugerida frecuentemente es utilizar java.lang.String.getBytes() como entrada a 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 el String en la memoria como una matriz de bytes, y en contra del propósito de una corriente. En la mayoría de los casos, esto no es gran cosa, pero estaba buscando algo que preservara la intención de una transmisión: que la menor cantidad posible de datos se (re) materialice en la memoria.

+0

@djb Just did so. –

Respuesta

2

Bueno, una forma posible es:

  • crear una canalización PipedOutputStream
  • a un PipedInputStream
  • Wrap un OutputStreamWriter alrededor del PipedOutputStream (se puede especificar la codificación en el constructor)
  • Et voil & aacute ;, ¡todo lo que escriba en el OutputStreamWriter se puede leer en el PipedInputStream!

Por supuesto, esto parece una forma bastante hackosa de hacerlo, pero al menos es una manera.

+1

Interesante ... por supuesto, con esta solución creo que materializaría toda la cadena en la memoria o sufriría inanición en el hilo de lectura. Todavía espero que haya una implementación real en alguna parte. –

+4

Tiene que tener cuidado con el Flujo de Tubería (Entrada | Salida). De acuerdo con los documentos: "... Intentar utilizar ambos objetos de un solo hilo no es recomendable, ya que puede bloquear el hilo ..." http://java.sun.com/j2se/1.4.2/docs/ api/java/io/PipedInputStream.html –

2

Una solución es rodar su propia, la creación de una implementación InputStream que probablemente utilice java.nio.charset.CharsetEncoder para codificar cada char o trozo de char s a una matriz de bytes para el InputStream como sea necesario.

+1

Hacer cosas de un personaje a la vez es costoso. Es por eso que tenemos "iteradores fragmentados" como InputStream que nos permiten leer un buffer a la vez. –

+0

Estoy de acuerdo con Tom - usted ** realmente ** no quiere hacer este personaje a la vez. – Eddie

+1

A menos que los datos sean realmente pequeños, y otras cosas (latencia de red, por ejemplo) tardan más. Entonces no importa. :) –

3

En mi opinión, la forma más fácil de hacerlo es presionando los datos a través de un escritor:

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 JVM estoy usando empujado datos a través de 8K trozos, pero que podría tener algún efecto en el tamaño del búfer reduciendo el número de caracteres escritos a la vez y llamando al color.


Una alternativa a la escritura de su propio envoltorio CharsetEncoder utilizar un escritor para codificar los datos, aunque es algo de dolor para hacer lo correcto.Esto debería ser una aplicación fiable (si es 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); 
    } 

    } 

} 
+3

Esta solución "más fácil" requiere mucho código. – kurtzbot

16

Si no te importa una dependencia en el paquete de commons-io, a continuación, se puede utilizar el método de IOUtils.toInputStream(String text).

+9

En ese caso, agrega una dependencia que no hace más que 'devolver el nuevo ByteArrayInputStream (input.getBytes()); ' ¿Realmente vale la pena una dependencia? Honestamente, no, no lo es. – whaefelinger

+1

Es verdad, además de que es exactamente la solución alternativa que el operador no quiere usar porque no quiere "materializar el hilo en la memoria" opuesto a que el hilo se materialice en otro lugar del sistema :) –

+0

¿Tenemos cualquier biblioteca que convierta un objeto personalizado en fuente de flujo de entrada; algo así como IOUtils.toInputStream (objeto MyObject)? –

72

Actualización: Esta respuesta es precisamente lo que OP no desea. Por favor, lea las otras respuestas.

Para aquellos casos en que no se preocupan por los datos que están siendo re-materializado en la memoria, por favor utilice:

new ByteArrayInputStream(str.getBytes("UTF-8")) 
+3

La solución propuesta por esta respuesta ha sido anticipada, contemplada y rechazada por la pregunta. Entonces, en mi opinión, esta respuesta debe ser eliminada. –

+1

Puede que tengas razón. Originalmente lo hice un comentario probablemente porque no era una respuesta real a la pregunta de OP. –

+23

Como visitante que viene aquí por el título de la pregunta, estoy feliz de que esta respuesta esté aquí. Entonces: no elimine esta respuesta. La observación en la parte superior "Esta respuesta es precisamente lo que el OP no desea. Lea las otras respuestas". es suficiente. –

0

Sé que esto es una cuestión de edad, sino que tenía el mismo problema a mí mismo hoy, y este 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++); 
    } 
}; 
} 
+6

Esto no funciona para no ASCII. – slow

3

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

código 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

0

Usted puede tomar la ayuda de org.hsqldb.lib biblioteca.

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

En general, las preguntas son mucho más útiles si incluyen una explicación de lo que el código está destinado a hacer. – Peter

Cuestiones relacionadas