Como converter um Reader para InputStream e um Writer para OutputStream?

87

Existe uma maneira fácil de evitar lidar com problemas de codificação de texto?

Andrei Savu
fonte

Respostas:

45

Você realmente não pode evitar lidar com os problemas de codificação de texto, mas existem soluções no Apache Commons:

Você só precisa escolher a codificação de sua escolha.

Peter
fonte
7
Para sua informação: o código ReaderInputStream tem um bug na maneira como ele lê bytes (não funcionará para todas as codificações). Prova: ilegalargumentexception.blogspot.com/2009/05/… Há um bug aberto: issues.apache.org/bugzilla/show_bug.cgi?id=40455
McDowell
1
Você pode encontrar as classes na biblioteca commons-io do Apache: commons.apache.org/proper/commons-io
AlikElzin-kilaka
@McDowell, o bug que você mencionou está na implementação do Apache Ant, não no commons-io, então não é relevante para esta resposta.
Romano de
94

Se você está começando com uma String, também pode fazer o seguinte:

new ByteArrayInputStream(inputString.getBytes("UTF-8"))
Ritesh Tendulkar
fonte
7
Uma boa ReaderInputStreamimplementação exigiria menos memória - não deveria haver necessidade de armazenar todos os bytes em uma matriz de uma vez.
Piotr Findeisen
3
Eu gosto dessa solução porque ela funciona quando você precisa de um código de teste de unidade que aceita entrada (por exemplo) na entrada padrão.
Kedar Mhaswade
42

Bem, um Reader lida com caracteres e um InputStream lida com bytes. A codificação especifica como você deseja representar seus caracteres como bytes, portanto, você não pode realmente ignorar o problema. Quanto a evitar problemas, minha opinião é: escolha um conjunto de caracteres (por exemplo, "UTF-8") e continue com ele.

Em relação a como fazê-lo de fato, como foi apontado, " os nomes óbvios para essas classes são ReaderInputStream e WriterOutputStream . " Surpreendentemente, " eles não estão incluídos na biblioteca Java ", embora as classes 'opostas', InputStreamReader e OutputStreamWriter sejam incluído.

Portanto, muitas pessoas criaram suas próprias implementações, incluindo o Apache Commons IO . Dependendo dos problemas de licenciamento, você provavelmente poderá incluir a biblioteca commons-io em seu projeto ou até mesmo copiar uma parte do código-fonte (que pode ser baixado aqui ).

Como você pode ver, a documentação de ambas as classes afirma que "todas as codificações de conjunto de caracteres suportadas pelo JRE são tratadas corretamente".

NB Um comentário em uma das outras respostas aqui menciona esse bug . Mas isso afeta a classe Apache Ant ReaderInputStream ( aqui ), não a classe Apache Commons IO ReaderInputStream.

Peter Ford
fonte
19

Observe também que, se você está começando com uma String, você pode pular a criação de um StringReader e criar um InputStream em uma etapa usando org.apache.commons.io.IOUtils do Commons IO assim:

InputStream myInputStream = IOUtils.toInputStream(reportContents, "UTF-8");

Claro que você ainda precisa pensar na codificação do texto, mas pelo menos a conversão está acontecendo em uma etapa.

Phil Harvey
fonte
4
Esse método faz basicamente new ByteArrayInputStream(report.toString().getBytes("utf-8")), o que envolve a alocação de duas cópias adicionais do relatório na memória. Se o relatório for grande, é ruim. Veja minha resposta.
Oliv
8

Usar:

new CharSequenceInputStream(html, StandardCharsets.UTF_8);

Dessa forma, não é necessária uma conversão inicial para Stringe depois para byte[], o que aloca muito mais memória heap, caso o relatório seja grande. Ele é convertido em bytes instantaneamente conforme o fluxo é lido, direto do StringBuffer.

Ele usa CharSequenceInputStream do projeto Apache Commons IO.

Oliv
fonte
5

Os nomes óbvios para essas classes são ReaderInputStream e WriterOutputStream. Infelizmente, eles não estão incluídos na biblioteca Java. No entanto, o Google é seu amigo.

Não tenho certeza se isso vai resolver todos os problemas de codificação de texto, que são um pesadelo.

Existe um RFE, mas está fechado, não corrige.

Tom Hawtin - tackline
fonte
1
bugs.openjdk.java.net/browse/JDK-4103785 contém o comentário "temos uma API pública para codificação de conjunto de caracteres ... nenhuma razão convincente para adicionar essas classes" - então, como fazer isso no Java 7, sem adicionais bibliotecas, doze anos depois?
Piotr Findeisen
5

Você não pode evitar problemas de codificação de texto, mas o Apache commons-io tem

Observe que essas são as bibliotecas mencionadas na resposta de Peter de koders.com, apenas links para a biblioteca em vez do código-fonte.

dfrankow
fonte
4

Você está tentando escrever o conteúdo de um Readerpara um OutputStream? Em caso afirmativo, você terá mais facilidade em agrupar o OutputStreamem um OutputStreamWritere escrever os charde Readerem em Writer, em vez de tentar converter o leitor em InputStream:

final Writer writer = new BufferedWriter(new OutputStreamWriter( urlConnection.getOutputStream(), "UTF-8" ) );
int charsRead;
char[] cbuf = new char[1024];
while ((charsRead = data.read(cbuf)) != -1) {
    writer.write(cbuf, 0, charsRead);
}
writer.flush();
// don't forget to close the writer in a finally {} block
Sam Barnum
fonte
1

Um aviso ao usar WriterOutputStream - ele nem sempre lida com a gravação de dados binários em um arquivo corretamente / da mesma forma que um fluxo de saída regular. Eu tive um problema com isso que demorei um pouco para rastrear.

Se você puder, eu recomendo usar um fluxo de saída como sua base, e se você precisar escrever strings, use um wrapper OUtputStreamWriter ao redor do fluxo para fazer isso. É muito mais confiável converter texto em bytes do que o contrário, provavelmente porque WriterOutputStream não faz parte da biblioteca Java padrão

Romeara
fonte
-1

Para ler uma string em um fluxo usando apenas o que o java fornece.

InputStream s = new BufferedInputStream( new ReaderInputStream( new StringReader("a string")));
Aaron
fonte
6
ReaderInputStream está no Apache Commons IO.
Will Beason