Se você tem um java.io.InputStream
objeto, como deve processar esse objeto e produzir um String
?
Suponha que eu possua um InputStream
que contenha dados de texto e deseje convertê-lo em um String
, para que, por exemplo, eu possa gravá-lo em um arquivo de log.
Qual é a maneira mais fácil de obter InputStream
e convertê-lo em um 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 há de melhor em linguagem, o que nunca suporta conversões de tipo mágico como a que você descreveu.Respostas:
Uma boa maneira de fazer isso é usando commons Apache
IOUtils
para copiar oInputStream
em umStringWriter
... algo comoou mesmo
Como alternativa, você pode usar
ByteArrayOutputStream
se não quiser misturar seus Streams e Writersfonte
Resuma outras respostas, encontrei 11 maneiras principais de fazer isso (veja abaixo). E eu escrevi alguns testes de desempenho (veja os resultados abaixo):
Maneiras de converter um InputStream em uma String:
Usando
IOUtils.toString
(Apache Utils)Usando
CharStreams
(Goiaba)Usando
Scanner
(JDK)Usando a API de Stream (Java 8). Aviso : Esta solução converte quebras de linha diferentes (como
\r\n
) em\n
.Utilizando a API de Stream paralela (Java 8). Aviso : Esta solução converte quebras de linha diferentes (como
\r\n
) em\n
.Usando
InputStreamReader
eStringBuilder
(JDK)Usando
StringWriter
eIOUtils.copy
(Apache Commons)Usando
ByteArrayOutputStream
einputStream.read
(JDK)Usando
BufferedReader
(JDK). Aviso: Esta solução converte quebras de linha diferentes (como\n\r
) naline.separator
propriedade do sistema (por exemplo, no Windows para "\ r \ n").Usando
BufferedInputStream
eByteArrayOutputStream
(JDK)Usando
inputStream.read()
eStringBuilder
(JDK). Aviso : Esta solução tem problemas com Unicode, por exemplo, com texto em russo (funciona corretamente apenas com texto não-Unicode)Atenção :
As soluções 4, 5 e 9 convertem quebras de linha diferentes em uma.
A solução 11 não pode funcionar corretamente com texto Unicode
Testes de performance
Testes de desempenho para
String
url pequeno (comprimento = 175), no github (modo = Tempo médio, sistema = Linux, a pontuação 1.343 é a melhor):Testes de desempenho para
String
url grande (comprimento = 50100), no github (modo = Tempo médio, sistema = Linux, a pontuação 200.715 é a melhor):Gráficos (testes de desempenho dependendo do comprimento do fluxo de entrada no sistema Windows 7)
Teste de desempenho (tempo médio), dependendo do comprimento do fluxo de entrada no sistema Windows 7:
fonte
\r\n
) nas\n
quais podem ser indesejadas em alguns casos. Também seria bom ver a memória adicional necessária ou pelo menos a pressão de alocação (pelo menos você pode executar o JMH-prof gc
). Para um post realmente interessante, seria ótimo ver os gráficos (dependendo do comprimento da string dentro do mesmo tamanho de entrada e dependendo do tamanho da entrada dentro do mesmo comprimento de string).reset()
exemplo 11?Aqui está uma maneira de usar apenas a biblioteca Java padrão (observe que o fluxo não está fechado, sua milhagem pode variar).
Aprendi esse truque no artigo "Truques estúpidos do scanner" . A razão pela qual isso funciona é porque o Scanner interage com os tokens no fluxo e, nesse caso, separamos os tokens usando "início do limite de entrada" (\ A), fornecendo apenas um token para todo o conteúdo do fluxo.
Observe que, se você precisar ser específico sobre a codificação do fluxo de entrada, poderá fornecer o segundo argumento ao
Scanner
construtor que indica qual conjunto de caracteres usar (por exemplo, "UTF-8").A gorjeta do chapéu também vale para Jacob , que uma vez me indicou o referido artigo.
fonte
if (is == null) return "";
logo no início do método; Acredito que esta resposta precisa ser atualizada para lidar melhor com inputStreams nulos.try(java.util.Scanner s = new java.util.Scanner(is)) { return s.useDelimiter("\\A").hasNext() ? s.next() : ""; }
O Apache Commons permite:
Obviamente, você pode escolher outras codificações de caracteres além de UTF-8.
Veja também: ( documentação )
fonte
Levando em conta o arquivo, é preciso primeiro obter uma
java.io.Reader
instância. Isso pode ser lido e adicionado a umStringBuilder
(não precisamosStringBuffer
se não estiver acessando-o em vários threads eStringBuilder
for mais rápido). O truque aqui é que trabalhamos em blocos e, como tal, não precisa de outros fluxos de buffer. O tamanho do bloco é parametrizado para otimização do desempenho em tempo de execução.fonte
In our product, I even replaced
deveria ser 'até substituímos'.Usar:
fonte
readLine
lido caractere por caractere para procurar EOL. Além disso, se não houver quebra de linha no fluxo, isso realmente não faz sentido.Se você estiver usando o Google-Collections / Guava, poderá fazer o seguinte:
Observe que o segundo parâmetro (ou seja, Charsets.UTF_8) para o
InputStreamReader
não é necessário, mas geralmente é uma boa idéia especificar a codificação se você a conhece (o que você deve!)fonte
Esta é a melhor solução Java pura que se encaixa perfeitamente no Android e em qualquer outra JVM.
Esta solução funciona incrivelmente bem ... é simples, rápida e funciona em fluxos pequenos e grandes da mesma forma !! (veja a referência acima .. No. 8 )
fonte
2*n
que n é o tamanho do fluxo, conforme oByteArrayInputStream
sistema de crescimento automático.Para completar, aqui está a solução Java 9 :
O
readAllBytes
está atualmente em JDK 9 base de código principal, por isso provável que apareça no comunicado. Você pode experimentá-lo agora mesmo usando as construções de captura instantânea do JDK 9 .fonte
byte[] buf = new byte[DEFAULT_BUFFER_SIZE];
onde oMAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
que dáMAX_BUFFER_SIZE = 2147483639
. O Google diz que tem cerca de 2,147 GB.InputStream
, não sobrePath
. OInputStream
pode ser criado a partir de muitas fontes diferentes, não apenas de arquivos.byte[]
implementação se todos os caracteres estiverem nos primeiros 256 pontos de código. Isso significa que a nova String (byte [], "ISO-Latin-1") será uma cópia simples da matriz.Usar:
fonte
BufferedInputStream
. As leituras subjacentes são 8192 bytes de cada vez.BufferedInputStream
e ler em um buffer de matriz de bytes em vez de um byte por vez. Exemplo: 200ms vs 60ms ao ler um arquivo 4.56 MiB.buf.toString()
.Aqui está a solução mais elegante e pura de Java (sem biblioteca) que eu criei após algumas experiências:
fonte
InputStream
deve ser fechado pelo chamador.readLine
? Se você não usar as linhas por si só, o que é bom (excepto se está muito lento?)Fiz uma comparação com 14 respostas distintas aqui (desculpe por não fornecer créditos, mas há muitas duplicatas).
O resultado é muito surpreendente. Acontece que o Apache IOUtils é o mais lento e
ByteArrayOutputStream
as soluções mais rápidas:Então, primeiro, aqui está o melhor método:
Resultados de benchmark, de 20 MB de bytes aleatórios em 20 ciclos
Tempo em milissegundos
Código fonte de referência
fonte
Eu usaria alguns truques do Java 8.
Essencialmente, o mesmo que algumas outras respostas, exceto mais sucintas.
fonte
return null
chamado? Osbr.lines...
retornos ou uma exceção são lançados.parallel()
para o stream?\r\n
iria acabar ficando convertido em\n
...System.lineSeparator()
para usar o final de linha dependente da plataforma apropriado.Fiz alguns testes de tempo porque o tempo importa sempre.
Tentei obter a resposta em uma String 3 de maneiras diferentes. (mostrado abaixo)
Eu deixei de fora os blocos try / catch para facilitar a leitura.
Para dar contexto, este é o código anterior para todas as três abordagens:
1)
2)
3)
Portanto, depois de executar 500 testes em cada abordagem com os mesmos dados de solicitação / resposta, aqui estão os números. Mais uma vez, essas são minhas descobertas e suas descobertas podem não ser exatamente as mesmas, mas escrevi isso para dar uma indicação a outros das diferenças de eficiência dessas abordagens.
Classificações:
Abordagem nº 1
Abordagem nº 3 - 2,6% mais lento que nº 1
Abordagem nº 2 - 4,3% mais lento que nº 1
Qualquer uma dessas abordagens é uma solução apropriada para obter uma resposta e criar uma String a partir dela.
fonte
Solução Java pura usando Stream s, funciona desde o Java 8.
Como mencionado por Christoffer Hammarström abaixo de outras respostas , é mais seguro especificar explicitamente o Charset . Ou seja, o construtor InputStreamReader pode ser alterado da seguinte maneira:
fonte
Charset.forName("UTF-8")
, useStandardCharsets.UTF_8
(dejava.nio.charset
).Aqui está a resposta mais ou menos do sampath, limpa um pouco e representada como uma função:
fonte
Se você estava se sentindo aventureiro, pode misturar Scala e Java e acabar com isso:
A combinação de bibliotecas e códigos Java e Scala tem seus benefícios.
Veja a descrição completa aqui: Maneira linguística de converter um InputStream em uma String no Scala
fonte
Source.fromInputStream(...).mkString
Se você não pode usar o Commons IO (FileUtils / IOUtils / CopyUtils), veja um exemplo usando um BufferedReader para ler o arquivo linha por linha:
Ou, se você quiser velocidade bruta, proponho uma variação do que Paul de Vrieze sugeriu (o que evita o uso de um StringWriter (que usa um StringBuffer internamente)):
fonte
Este é legal porque:
Como fazer isso?
Para JDK 9
fonte
catch (Throwable)
realmente não deve estar vazio se este é o código de produção.Esta é uma resposta adaptada do
org.apache.commons.io.IOUtils
código-fonte para quem deseja ter a implementação do apache, mas não deseja a biblioteca inteira.fonte
Certifique-se de fechar os fluxos no final se você usar leitores de fluxo
EDIT: No JDK 7+, você pode usar a construção try-with-resources.
fonte
iStream
realmente deve ser fechado pelo chamador porque ele foi criadoiStream
. Além disso, os fluxos de fechamento devem ser feitos em umfinally
bloco ou, melhor ainda, em uma instrução try 7 com recursos do Java 7. No seu código, quandoreadLine()
lançaIOException
oubuilder.append()
lançaOutOfMemoryError
, os fluxos permaneceriam abertos.Outro, para todos os usuários do Spring:
Os métodos utilitários in
org.springframework.util.StreamUtils
são semelhantes aos métodos inFileCopyUtils
, mas deixam o fluxo aberto quando concluído.fonte
Use o java.io.InputStream.transferTo (OutputStream) suportado no Java 9 e o ByteArrayOutputStream.toString (String) que recebe o nome do conjunto de caracteres:
fonte
Aqui está o método completo para converter
InputStream
emString
sem usar nenhuma biblioteca de terceiros. UseStringBuilder
para ambiente de thread único, caso contrário, useStringBuffer
.fonte
in = new InputStreamReader(inputStream)
e(char)in.read()
.Veja como fazer isso usando apenas o JDK usando buffers de matriz de bytes. É assim
IOUtils.copy()
que todos os métodos commons-io funcionam. Você pode substituirbyte[]
porchar[]
se estiver copiando de um emReader
vez de umInputStream
.fonte
Os usuários do Kotlin simplesmente fazem:
enquanto que
é o método de extensão interno da biblioteca padrão Kotlin.
fonte
is.bufferedReader().use { it.readText() }
.A maneira mais fácil no JDK é com os seguintes snipplets de código.
fonte
Aqui está minha solução baseada em Java 8 , que usa a nova API de fluxo para coletar todas as linhas de
InputStream
:fonte
Em termos de
reduce
, econcat
pode ser expresso em Java 8 como:fonte
StringBuilder
pode ser mais eficiente. Vou verificar, mas meu objetivo era mostrar uma abordagem mais funcional com imutávelString
.Resposta JDK 7/8 que fecha o fluxo e ainda gera uma IOException:
fonte