¿Cómo leo/convierto un InputStream en una cadena en Java?
Si tiene un java.io.InputStream
objeto, ¿cómo debería procesarlo y producir un String
?
Supongamos que tengo un archivo InputStream
que contiene datos de texto y quiero convertirlo en un archivo String
, así, por ejemplo, puedo escribirlo en un archivo de registro.
¿ Cuál es la forma más fácil de tomar InputStream
y convertirlo en String
?
public String convertStreamToString(InputStream is) {
// ???
}
Para resumir las otras respuestas, encontré 11 formas principales de hacer esto (ver más abajo). Y escribí algunas pruebas de rendimiento (ver resultados a continuación):
Formas de convertir un InputStream en una cadena:
Usando
IOUtils.toString
(Utilidades de Apache)String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
Usando
CharStreams
(guayaba)String result = CharStreams.toString(new InputStreamReader( inputStream, Charsets.UTF_8));
Usando
Scanner
(JDK)Scanner s = new Scanner(inputStream).useDelimiter("\\A"); String result = s.hasNext() ? s.next() : "";
Usando Stream API (Java 8). Advertencia : esta solución convierte diferentes saltos de línea (como
\r\n
) a archivos\n
.String result = new BufferedReader(new InputStreamReader(inputStream)) .lines().collect(Collectors.joining("\n"));
Usando Stream API paralelo (Java 8). Advertencia : esta solución convierte diferentes saltos de línea (como
\r\n
) a archivos\n
.String result = new BufferedReader(new InputStreamReader(inputStream)) .lines().parallel().collect(Collectors.joining("\n"));
Usando
InputStreamReader
yStringBuilder
(JDK)int bufferSize = 1024; char[] buffer = new char[bufferSize]; StringBuilder out = new StringBuilder(); Reader in = new InputStreamReader(stream, StandardCharsets.UTF_8); for (int numRead; (numRead = in.read(buffer, 0, buffer.length)) > 0; ) { out.append(buffer, 0, numRead); } return out.toString();
Usando
StringWriter
yIOUtils.copy
(Apache Commons)StringWriter writer = new StringWriter(); IOUtils.copy(inputStream, writer, "UTF-8"); return writer.toString();
Usando
ByteArrayOutputStream
yinputStream.read
(JDK)ByteArrayOutputStream result = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; for (int length; (length = inputStream.read(buffer)) != -1; ) { result.write(buffer, 0, length); } // StandardCharsets.UTF_8.name() > JDK 7 return result.toString("UTF-8");
Usando
BufferedReader
(JDK). Advertencia: esta solución convierte diferentes saltos de línea (como\n\r
) enline.separator
propiedades del sistema (por ejemplo, en Windows en "\r\n").String newLine = System.getProperty("line.separator"); BufferedReader reader = new BufferedReader( new InputStreamReader(inputStream)); StringBuilder result = new StringBuilder(); for (String line; (line = reader.readLine()) != null; ) { if (result.length() > 0) { result.append(newLine); } result.append(line); } return result.toString();
Usando
BufferedInputStream
yByteArrayOutputStream
(JDK)BufferedInputStream bis = new BufferedInputStream(inputStream); ByteArrayOutputStream buf = new ByteArrayOutputStream(); for (int result = bis.read(); result != -1; result = bis.read()) { buf.write((byte) result); } // StandardCharsets.UTF_8.name() > JDK 7 return buf.toString("UTF-8");
Usando
inputStream.read()
yStringBuilder
(JDK). Advertencia : esta solución tiene problemas con Unicode, por ejemplo con texto en ruso (funciona correctamente solo con texto que no es Unicode)StringBuilder sb = new StringBuilder(); for (int ch; (ch = inputStream.read()) != -1; ) { sb.append((char) ch); } return sb.toString();
Advertencia :
Las soluciones 4, 5 y 9 convierten diferentes saltos de línea en uno.
La solución 11 no funciona correctamente con texto Unicode
Pruebas de rendimiento
Pruebas de rendimiento para String
URL pequeñas (longitud = 175) en github (modo = Tiempo promedio, sistema = Linux, la puntuación 1343 es la mejor):
Benchmark Mode Cnt Score Error Units
8. ByteArrayOutputStream and read (JDK) avgt 10 1,343 ± 0,028 us/op
6. InputStreamReader and StringBuilder (JDK) avgt 10 6,980 ± 0,404 us/op
10. BufferedInputStream, ByteArrayOutputStream avgt 10 7,437 ± 0,735 us/op
11. InputStream.read() and StringBuilder (JDK) avgt 10 8,977 ± 0,328 us/op
7. StringWriter and IOUtils.copy (Apache) avgt 10 10,613 ± 0,599 us/op
1. IOUtils.toString (Apache Utils) avgt 10 10,605 ± 0,527 us/op
3. Scanner (JDK) avgt 10 12,083 ± 0,293 us/op
2. CharStreams (guava) avgt 10 12,999 ± 0,514 us/op
4. Stream Api (Java 8) avgt 10 15,811 ± 0,605 us/op
9. BufferedReader (JDK) avgt 10 16,038 ± 0,711 us/op
5. parallel Stream Api (Java 8) avgt 10 21,544 ± 0,583 us/op
Pruebas de rendimiento para URL grandes String
(longitud = 50100) en github (modo = Tiempo promedio, sistema = Linux, puntuación 200,715 es la mejor):
Benchmark Mode Cnt Score Error Units
8. ByteArrayOutputStream and read (JDK) avgt 10 200,715 ± 18,103 us/op
1. IOUtils.toString (Apache Utils) avgt 10 300,019 ± 8,751 us/op
6. InputStreamReader and StringBuilder (JDK) avgt 10 347,616 ± 130,348 us/op
7. StringWriter and IOUtils.copy (Apache) avgt 10 352,791 ± 105,337 us/op
2. CharStreams (guava) avgt 10 420,137 ± 59,877 us/op
9. BufferedReader (JDK) avgt 10 632,028 ± 17,002 us/op
5. parallel Stream Api (Java 8) avgt 10 662,999 ± 46,199 us/op
4. Stream Api (Java 8) avgt 10 701,269 ± 82,296 us/op
10. BufferedInputStream, ByteArrayOutputStream avgt 10 740,837 ± 5,613 us/op
3. Scanner (JDK) avgt 10 751,417 ± 62,026 us/op
11. InputStream.read() and StringBuilder (JDK) avgt 10 2919,350 ± 1101,942 us/op
Gráficos (pruebas de rendimiento según la longitud del flujo de entrada en el sistema Windows 7)
Prueba de rendimiento (tiempo promedio) según la longitud del flujo de entrada en el sistema Windows 7:
length 182 546 1092 3276 9828 29484 58968
test8 0.38 0.938 1.868 4.448 13.412 36.459 72.708
test4 2.362 3.609 5.573 12.769 40.74 81.415 159.864
test5 3.881 5.075 6.904 14.123 50.258 129.937 166.162
test9 2.237 3.493 5.422 11.977 45.98 89.336 177.39
test6 1.261 2.12 4.38 10.698 31.821 86.106 186.636
test7 1.601 2.391 3.646 8.367 38.196 110.221 211.016
test1 1.529 2.381 3.527 8.411 40.551 105.16 212.573
test3 3.035 3.934 8.606 20.858 61.571 118.744 235.428
test2 3.136 6.238 10.508 33.48 43.532 118.044 239.481
test10 1.593 4.736 7.527 20.557 59.856 162.907 323.147
test11 3.913 11.506 23.26 68.644 207.591 600.444 1211.545
Una buena manera de hacer esto es usar Apache Commons IOUtils
para copiarlo InputStream
en un StringWriter
... Algo así como
StringWriter writer = new StringWriter();
IOUtils.copy(inputStream, writer, encoding);
String theString = writer.toString();
o incluso
// NB: does not close inputStream, you'll have to use try-with-resources for that
String theString = IOUtils.toString(inputStream, encoding);
Alternativamente, puede usarlo ByteArrayOutputStream
si no desea mezclar sus Streams y Writers.
Aquí hay una manera de usar solo la biblioteca Java estándar (tenga en cuenta que la transmisión no está cerrada, su kilometraje puede variar).
static String convertStreamToString(java.io.InputStream is) {
java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}
Aprendí este truco del artículo "Trucos estúpidos del escáner" . La razón por la que funciona es porque Scanner itera sobre los tokens en la secuencia y, en este caso, separamos los tokens usando el "comienzo del límite de entrada" (\A), lo que nos da solo un token para todo el contenido de la secuencia.
Tenga en cuenta que si necesita ser específico acerca de la codificación del flujo de entrada, puede proporcionar el segundo argumento al Scanner
constructor que indique qué juego de caracteres usar (por ejemplo, "UTF-8").
Un saludo también para Jacob , quien una vez me señaló dicho artículo.
Apache Commons permite:
String myString = IOUtils.toString(myInputStream, "UTF-8");
Por supuesto, puedes elegir otras codificaciones de caracteres además de UTF-8.
Ver también: ( documentación )