Как преобразовать Reader в InputStream и Writer в OutputStream?

87

Есть ли простой способ избежать проблем с кодировкой текста?

Андрей Саву
источник

Ответы:

45

Вы действительно не можете избежать проблем с кодировкой текста, но в Apache Commons есть существующие решения:

Вам просто нужно выбрать кодировку по вашему выбору.

Питер
источник
7
К вашему сведению: код ReaderInputStream имеет ошибку в способе чтения байтов (он не будет работать для всех кодировок). Доказательство: незаконныйargumentexception.blogspot.com/2009/05/ ... Есть открытая ошибка: issues.apache.org/bugzilla/show_bug.cgi?id=40455
МакДауэлл
1
Вы можете найти классы в библиотеке Apache commons-io: commons.apache.org/proper/commons-io
AlikElzin-kilaka
@McDowell, ошибка, о которой вы упомянули, находится в реализации Apache Ant, а не в commons-io, поэтому она не имеет отношения к этому ответу.
Роман
94

Если вы начинаете со String, вы также можете сделать следующее:

new ByteArrayInputStream(inputString.getBytes("UTF-8"))
Ритеш Тендулкар
источник
7
Хорошая ReaderInputStreamреализация потребует меньше памяти - не должно быть необходимости хранить все байты в массиве сразу.
Петр Финдейсен,
3
Мне нравится это решение, потому что оно работает, когда вам нужен код модульного тестирования, который принимает ввод, например, на стандартный ввод.
Kedar Mhaswade
43

Итак, Reader имеет дело с символами, а InputStream - с байтами. Кодировка определяет, как вы хотите представлять свои символы в байтах, поэтому вы не можете игнорировать проблему. Что касается избежания проблем, мое мнение таково: выберите одну кодировку (например, "UTF-8") и придерживайтесь ее.

Что касается того, как это сделать на самом деле, как уже указывалось, « очевидными именами для этих классов являются ReaderInputStream и WriterOutputStream . » Удивительно, но « они не включены в библиотеку Java », хотя «противоположные» классы, InputStreamReader и OutputStreamWriter, являются включены.

Итак, многие люди придумали свои собственные реализации, включая Apache Commons IO . В зависимости от проблем с лицензированием вы, вероятно, сможете включить библиотеку commons-io в свой проект или даже скопировать часть исходного кода (который можно загрузить здесь ).

Как видите, в документации обоих классов указано, что «все кодировки кодировки, поддерживаемые JRE, обрабатываются правильно».

NB В комментарии к одному из других ответов здесь упоминается эта ошибка . Но это влияет на класс Apache Ant ReaderInputStream ( здесь ), а не на класс Apache Commons IO ReaderInputStream.

Питер Форд
источник
19

Также обратите внимание, что если вы начинаете со String, вы можете пропустить создание StringReader и создать InputStream за один шаг, используя org.apache.commons.io.IOUtils из Commons IO следующим образом:

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

Конечно, вам все равно нужно подумать о кодировке текста, но, по крайней мере, преобразование происходит за один шаг.

Фил Харви
источник
4
В основном это метод new ByteArrayInputStream(report.toString().getBytes("utf-8")), который включает размещение двух дополнительных копий отчета в памяти. Если отчет большой - плохой. Смотрите мой ответ.
Oliv
8

Использование:

new CharSequenceInputStream(html, StandardCharsets.UTF_8);

Этот способ не требует предварительного преобразования в, Stringа затем в byte[], который выделяет намного больше памяти кучи, если отчет большой. Он преобразуется в байты на лету, когда поток читается, прямо из StringBuffer.

Он использует CharSequenceInputStream из проекта ввода-вывода Apache Commons.

Олив
источник
5

Очевидные имена для этих классов - ReaderInputStream и WriterOutputStream. К сожалению, они не включены в библиотеку Java. Однако Google - ваш друг.

Я не уверен, что он решит все проблемы с кодировкой текста, которые просто кошмарны.

Есть RFE, но закрытый, исправлять не буду.

Том Хотин - tackline
источник
1
bugs.openjdk.java.net/browse/JDK-4103785 содержит комментарий «у нас есть общедоступный API для кодирования наборов символов ... нет веских причин для добавления этих классов» - так как это сделать в Java 7, без дополнительных библиотеки, через двенадцать лет?
Петр Финдейзен
5

Вы не можете избежать проблем с кодировкой текста, но Apache commons-io имеет

Обратите внимание, что это библиотеки, упомянутые в ответе Питера на koders.com, просто ссылки на библиотеку, а не на исходный код.

dfrankow
источник
4

Вы пытаетесь записать содержимое a Readerв OutputStream? Если это так, вам будет легче обернуть OutputStreamв OutputStreamWriterи записать chars из в Readerв Writer, вместо того, чтобы пытаться преобразовать читателя в 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
Сэм Барнум
источник
1

Предупреждение при использовании WriterOutputStream - он не всегда обрабатывает запись двоичных данных в файл должным образом / так же, как обычный выходной поток. У меня была проблема с этим, и мне потребовалось некоторое время, чтобы ее разыскать.

Если вы можете, я бы рекомендовал использовать выходной поток в качестве основы, а если вам нужно писать строки, используйте для этого оболочку OUtputStreamWriter вокруг потока. Гораздо надежнее преобразовать текст в байты, чем наоборот, поэтому, вероятно, WriterOutputStream не является частью стандартной библиотеки Java.

Ромеара
источник
-1

Для чтения строки в потоке, используя только то, что предоставляет java.

InputStream s = new BufferedInputStream( new ReaderInputStream( new StringReader("a string")));
Аарон
источник
6
ReaderInputStream находится в Apache Commons IO.
Уилл Бисон,