from - ¿Cómo puedo obtener un java.io.InputStream de un java.lang.String?
inputstreamreader to inputstream java (8)
Tengo un String
que quiero usar como InputStream
. En Java 1.0, puede usar java.io.StringBufferInputStream
, pero eso ha sido @Deprecrated
(por 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 clase
StringReader
.
Puede crear un java.io.Reader
con java.io.StringReader
, pero no hay adaptadores para tomar un Reader
y crear un InputStream
.
Encontré un error antiguo que pedía un reemplazo adecuado, pero no existe tal cosa, por lo que puedo decir.
La solución sugerida frecuentemente es usar 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 toda la String
en 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 una transmisión: que la menor cantidad posible de datos se (re) materialice en la memoria.
Bueno, una forma posible es:
- Crea un
PipedOutputStream
-
PipedInputStream
a unPipedInputStream
-
PipedOutputStream
unOutputStreamWriter
alrededor dePipedOutputStream
(puede especificar la codificación en el constructor) - ¡Et voilá, todo lo que escribes en
OutputStreamWriter
se puede leer desdePipedInputStream
!
Por supuesto, esto parece una forma bastante hackosa de hacerlo, pero al menos es una manera.
En mi opinión, la manera 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 de JVM que estoy utilizando empujó los datos a través de fragmentos de 8K, pero 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 escribir su propio envoltorio CharsetEncoder para usar un Escritor para codificar los datos, aunque es algo doloroso hacer lo correcto. Esta debería ser una implementación confiable (si 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);
}
}
}
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://.com/a/27909221/5658642
Puede tomar la ayuda de la biblioteca org.hsqldb.lib.
public StringInputStream(String paramString)
{
this.str = paramString;
this.available = (paramString.length() * 2);
}
Sé que esta es una vieja pregunta, pero tuve el mismo problema hoy, 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++);
}
};
}
Si no le importa una dependencia en el paquete commons-io , puede usar el método IOUtils.toInputStream (String text) .
Una solución es java.nio.charset.CharsetEncoder
suya, creando una implementación de InputStream
que probablemente usaría java.nio.charset.CharsetEncoder
para codificar cada char
o fragmento de char
a una matriz de bytes para el InputStream
según sea necesario.
Actualización: esta respuesta es precisamente lo que el OP no quiere. Por favor, lea las otras respuestas.
Para aquellos casos en los que no nos importa que los datos se vuelvan a materializar en la memoria, use:
new ByteArrayInputStream(str.getBytes("UTF-8"))