Si tiene un java.io.InputStream
objeto, ¿cómo debe procesar ese objeto y producir un String
?
Supongamos que tengo un archivo InputStream
que contiene datos de texto y quiero convertirlo String
en un archivo, por ejemplo, puedo escribirlo en un archivo de registro.
¿Cuál es la forma más fácil de tomar InputStream
y convertirlo en a String
?
public String convertStreamToString(InputStream is) {
// ???
}
ByteArrayOutputStream outputBytes = new ByteArrayOutputStream();
for(byte[] b = new byte[512]; 0 < inputStream.read(b); outputBytes.write(b));
return new String(outputBytes.toByteArray(), StandardCharsets.UTF_8);
String s = Files.readString(Path.of("SomeFile.txt"));
que es tan bueno como puede obtener un lenguaje, que nunca admitirá conversiones de tipo mágico como la que describió.Respuestas:
Una buena manera de hacer esto es usar los recursos comunes de Apache
IOUtils
para copiarlosInputStream
enStringWriter
... algo asío incluso
Alternativamente, podría usarlo
ByteArrayOutputStream
si no desea mezclar sus Streams y Escritoresfuente
Resuma otras respuestas. Encontré 11 formas principales de hacer esto (ver más abajo). Y escribí algunas pruebas de rendimiento (ver los resultados a continuación):
Formas de convertir un InputStream en una cadena:
Utilizando
IOUtils.toString
(Apache Utils)Usando
CharStreams
(guayaba)Usando
Scanner
(JDK)Usando Stream API (Java 8). Advertencia : esta solución convierte diferentes saltos de línea (como
\r\n
) a\n
.Utilizando la API Stream paralela (Java 8). Advertencia : esta solución convierte diferentes saltos de línea (como
\r\n
) a\n
.Usando
InputStreamReader
yStringBuilder
(JDK)Uso
StringWriter
yIOUtils.copy
(Apache Commons)Usando
ByteArrayOutputStream
yinputStream.read
(JDK)Usando
BufferedReader
(JDK). Advertencia: esta solución convierte diferentes saltos de línea (como\n\r
) a laline.separator
propiedad del sistema (por ejemplo, en Windows a "\ r \ n").Usando
BufferedInputStream
yByteArrayOutputStream
(JDK)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)Advertencia :
Las soluciones 4, 5 y 9 convierten diferentes saltos de línea en uno.
La solución 11 no puede funcionar correctamente con texto Unicode
Pruebas de rendimiento
Pruebas de rendimiento para pequeño
String
(longitud = 175), url en github (modo = Tiempo promedio, sistema = Linux, puntaje 1,343 es el mejor):Pruebas de rendimiento para grandes
String
(longitud = 50100), url en github (modo = Tiempo promedio, sistema = Linux, el puntaje 200,715 es el mejor):Gráficos (pruebas de rendimiento que dependen de la longitud del flujo de entrada en el sistema Windows 7)
Prueba de rendimiento (tiempo promedio) dependiendo de la longitud del flujo de entrada en el sistema Windows 7:
fuente
\r\n
) a los\n
que en algunos casos podrían no ser deseados. También sería bueno ver la memoria adicional requerida o al menos la presión de asignación (al menos puede ejecutar JMH con-prof gc
). Para la publicación realmente genial, sería genial ver los gráficos (dependiendo de la longitud de la cadena dentro del mismo tamaño de entrada y dependiendo del tamaño de entrada dentro de la misma longitud de cadena).reset()
Para qué sirve el ejemplo 11?Aquí hay una manera de usar solo la biblioteca estándar de Java (tenga en cuenta que la transmisión no está cerrada, su kilometraje puede variar).
Aprendí este truco del artículo "Trucos de Stupid Scanner" . La razón por la que funciona es porque Scanner itera sobre tokens en el flujo, 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 del flujo.
Tenga en cuenta que si necesita ser específico sobre la codificación de la secuencia de entrada, puede proporcionar el segundo argumento al
Scanner
constructor que indica qué conjunto de caracteres usar (por ejemplo, "UTF-8").La punta del sombrero también va para Jacob , quien una vez me señaló el artículo mencionado.
fuente
if (is == null) return "";
justo al comienzo del método; Creo que esta respuesta debe actualizarse para manejar mejor inputStreams nulos.try(java.util.Scanner s = new java.util.Scanner(is)) { return s.useDelimiter("\\A").hasNext() ? s.next() : ""; }
Apache Commons permite:
Por supuesto, puede elegir otras codificaciones de caracteres además de UTF-8.
Ver también: ( documentación )
fuente
Teniendo en cuenta el archivo uno, primero debe obtener una
java.io.Reader
instancia. Esto se puede leer y agregar a unStringBuilder
(no necesitamosStringBuffer
si no estamos accediendo a él en múltiples hilos, yStringBuilder
es más rápido). El truco aquí es que trabajamos en bloques y, como tal, no necesitamos otras secuencias de almacenamiento en búfer. El tamaño del bloque está parametrizado para la optimización del rendimiento en tiempo de ejecución.fuente
In our product, I even replaced
debería ser "incluso reemplazamos".Utilizar:
fuente
readLine
lee carácter por carácter para buscar EOL. Además, si no hay un salto de línea en la secuencia, esto realmente no tiene sentido.Si está utilizando Google-Collections / Guava, puede hacer lo siguiente:
Tenga en cuenta que el segundo parámetro (es decir, Charsets.UTF_8) para el
InputStreamReader
no es necesario, pero generalmente es una buena idea especificar la codificación si la conoce (¡lo cual debería!)fuente
Esta es la mejor solución Java pura que se adapta perfectamente para Android y cualquier otra JVM.
Esta solución funciona increíblemente bien ... ¡es simple, rápida y funciona en transmisiones pequeñas y grandes de la misma manera! (ver punto de referencia arriba .. No. 8 )
fuente
2*n
, donde n es el tamaño de la secuencia, según elByteArrayInputStream
sistema de crecimiento automático.Para completar, aquí está la solución Java 9 :
El
readAllBytes
es actualmente en el JDK 9 código base principal, por lo que es probable que aparezca en el comunicado. Puede probarlo ahora mismo utilizando las compilaciones de instantáneas JDK 9 .fuente
byte[] buf = new byte[DEFAULT_BUFFER_SIZE];
donde loMAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
que daMAX_BUFFER_SIZE = 2147483639
. Google dice que es alrededor de 2.147 GB.InputStream
, no sobrePath
. SeInputStream
pueden crear desde muchas fuentes diferentes, no solo archivos.byte[]
implementación si todos los caracteres están en los primeros 256 puntos de código. Esto significa que la nueva cadena (byte [], "ISO-Latin-1") será una simple copia de matriz.Utilizar:
fuente
BufferedInputStream
. Las lecturas subyacentes son 8192 bytes a la vez.BufferedInputStream
y leer en un búfer de matriz de bytes en lugar de un byte a la vez. Ejemplo: 200 ms frente a 60 ms al leer un archivo MiB 4.56.buf.toString()
.Aquí está la solución más elegante y pura de Java (sin biblioteca) que se me ocurrió después de un poco de experimentación:
fuente
InputStream
debe ser cerrado por la persona que llama.readLine
? si no usa las líneas per se, ¿de qué sirve (excepto ser muy lento?)Hice un punto de referencia sobre 14 respuestas distintas aquí (perdón por no proporcionar créditos, pero hay demasiados duplicados).
El resultado es muy sorprendente. Resulta que Apache IOUtils es la
ByteArrayOutputStream
solución más lenta y más rápida:Así que primero aquí está el mejor método:
Resultados de referencia, de 20 MB de bytes aleatorios en 20 ciclos
Tiempo en milisegundos.
Código fuente de referencia
fuente
Yo usaría algunos trucos de Java 8.
Esencialmente lo mismo que algunas otras respuestas, excepto más sucinto.
fuente
return null
Alguna vez te llamarían? Sebr.lines...
arrojan las devoluciones o una excepción.parallel()
al stream?\r\n
terminaría convirtiéndose en\n
...System.lineSeparator()
para usar el final de línea apropiado dependiente de la plataforma.Realicé algunas pruebas de tiempo porque el tiempo siempre importa.
Intenté obtener la respuesta en una cadena de 3 formas diferentes. (se muestra a continuación)
Dejé bloques try / catch para facilitar la lectura.
Para dar contexto, este es el código anterior para los 3 enfoques:
1)
2)
3)
Entonces, después de ejecutar 500 pruebas en cada enfoque con los mismos datos de solicitud / respuesta, aquí están los números. Una vez más, estos son mis hallazgos y sus hallazgos pueden no ser exactamente los mismos, pero escribí esto para dar alguna indicación a otros de las diferencias de eficiencia de estos enfoques.
Rangos:
Enfoque n. ° 1
Enfoque n. ° 3: 2.6% más lento que el n. ° 1
Enfoque n. ° 2: 4.3% más lento que el n. ° 1
Cualquiera de estos enfoques es una solución adecuada para obtener una respuesta y crear una cadena a partir de ella.
fuente
La solución Java pura que utiliza Stream s funciona desde Java 8.
Como mencionó Christoffer Hammarström debajo de otra respuesta , es más seguro especificar explícitamente el juego de caracteres . Es decir, el constructor InputStreamReader se puede cambiar de la siguiente manera:
fuente
Charset.forName("UTF-8")
, usaStandardCharsets.UTF_8
(fromjava.nio.charset
).Aquí está la respuesta de sampath más o menos, limpiada un poco y representada como una función:
fuente
Si te sientes aventurero, podrías mezclar Scala y Java y terminar con esto:
Mezclar código y bibliotecas Java y Scala tiene sus beneficios.
Vea la descripción completa aquí: forma idiomática de convertir un InputStream en una cadena en Scala
fuente
Source.fromInputStream(...).mkString
Si no puede usar Commons IO (FileUtils / IOUtils / CopyUtils), aquí hay un ejemplo usando un BufferedReader para leer el archivo línea por línea:
O si desea velocidad bruta, le propondría una variación de lo que sugirió Paul de Vrieze (que evita usar un StringWriter (que usa un StringBuffer internamente):
fuente
Este es bueno porque:
¿Cómo hacerlo?
Para JDK 9
fuente
catch (Throwable)
realidad no debería estar vacío si este es el código de producción.Esta es una respuesta adaptada del
org.apache.commons.io.IOUtils
código fuente , para aquellos que desean tener la implementación de apache pero no quieren la biblioteca completa.fuente
Asegúrese de cerrar las transmisiones al final si usa Stream Readers
EDITAR: en JDK 7+, puede usar la construcción de prueba con recursos.
fuente
iStream
la persona que llama realmente debería cerrarla porque creó la persona que llamaiStream
. Además, el cierre de flujos debe hacerse en unfinally
bloque, o incluso mejor en una declaración de prueba con recursos de Java 7. En su código, cuandoreadLine()
lanzaIOException
obuilder.append()
lanzaOutOfMemoryError
, las transmisiones permanecerían abiertas.Otro, para todos los usuarios de Spring:
Los métodos de utilidad en
org.springframework.util.StreamUtils
son similares a los deFileCopyUtils
, pero dejan la secuencia abierta cuando se hace.fuente
Utilice java.io.InputStream.transferTo (OutputStream) compatible con Java 9 y ByteArrayOutputStream.toString (String) que toma el nombre del conjunto de caracteres:
fuente
Aquí está el método completo para la conversión
InputStream
enString
sin necesidad de utilizar cualquier biblioteca de terceros. ÚseloStringBuilder
para un entorno de subproceso único; de lo contrario, úseloStringBuffer
.fuente
in = new InputStreamReader(inputStream)
y(char)in.read()
.Aquí le mostramos cómo hacerlo utilizando solo el JDK utilizando búferes de matriz de bytes. Así es como
IOUtils.copy()
funcionan todos los métodos commons-io . Puede reemplazarbyte[]
conchar[]
si está copiando desde un enReader
lugar de unInputStream
.fuente
Los usuarios de Kotlin simplemente hacen:
mientras
es el método de extensión incorporado de la biblioteca estándar de Kotlin.
fuente
is.bufferedReader().use { it.readText() }
.La forma más fácil en JDK es con los siguientes fragmentos de código.
fuente
Aquí está mi solución basada en Java 8 , que utiliza la nueva API Stream para recopilar todas las líneas de un
InputStream
:fuente
En términos de
reduce
, yconcat
se puede expresar en Java 8 como:fuente
StringBuilder
Podría ser más eficiente. Lo comprobaré, pero mi punto era mostrar un enfoque más funcional con inmutableString
.Respuesta de JDK 7/8 que cierra la transmisión y aún arroja una IOException:
fuente