OutputStreamWriter: примеры (JAVA)
OutputStreamWriter(OutputStream out, String charsetName)Описание класса OutputStreamWriter
Класс OutputStreamWriter из пакета java.io представляет адаптер, преобразующий поток символов в поток байтов. Он наследуется от Writer и служит для записи символов в поток байтов с указанной кодировкой. Основная идея - принимать символы (UTF-16 в Java) и кодировать их в последовательность байтов, отправляемую в связанный OutputStream.
Типичные случаи использования: запись текстовых данных в файлы, сетевые соединения, буферы байтов и системный вывод при необходимости контроля кодировки.
Доступные конструкторы и их параметры:
OutputStreamWriter(OutputStream out)- связывает писатель с указанным байтовым потоком с использованием кодировки по умолчанию платформы. Параметр:out- не должен быть null; при попытке использовать null возникаетNullPointerException.OutputStreamWriter(OutputStream out, String charsetName)- связывает писатель с указанным потоком и кодировкой по имени. Параметры:out,charsetName. При передаче несуществующей кодировки бросаетсяUnsupportedEncodingException.OutputStreamWriter(OutputStream out, java.nio.charset.Charset cs)- аналогично предыдущему, но кодировка задается объектомCharset. Параметры:out,cs. Null вызываетNullPointerException.
Основные методы и их поведение:
void write(int c)- записывает один символ (целое значение) в поток. Возвращаемое значение отсутствует. Может бросатьIOException.void write(char[] cbuf, int off, int len)- записывает часть массива символов.void write(String str, int off, int len)- записывает часть строки.Writer append(char c)и перегрузкиappend(CharSequence)- добавляют символы и возвращают сам объектWriter(обычноOutputStreamWriter), что позволяет цепочку вызовов.void flush()- принудительное преобразование оставшихся символов в байты и запись их в связанныйOutputStream. ВыбрасываетIOExceptionпри ошибке ввода-вывода.void close()- закрывает писатель и связанный поток. Внутри происходит flush, затем закрытие базового потока.String getEncoding()- возвращает имя текущей кодировки (возможноnull, если поток закрыт или кодировка неизвестна).
Поведение при невозможности закодировать знак. По умолчанию для большинства кодировок используется поведение замены символа на код символа замены (например, '?'). Для специального управления преобразованием необходимо использовать CharsetEncoder напрямую или другие инструменты.
Взаимодействие с буферизацией и другими обёртками. OutputStreamWriter не обеспечивает большого внутреннего буфера символов; при записи большого объёма рекомендуется оборачивать его в BufferedWriter для уменьшения системных вызовов. Если требуется удобство форматирования, предпочтительнее PrintWriter, но у него иные политики обработки исключений (по умолчанию подавляются).
Короткие примеры использования
Пример 1. Запись в память в кодировке UTF-8 и вывод результата.
import java.io.*;
import java.nio.charset.StandardCharsets;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (OutputStreamWriter osw = new OutputStreamWriter(baos, StandardCharsets.UTF_8)) {
osw.write("Привет");
osw.flush();
}
String s = baos.toString("UTF-8");
System.out.println(s);
Привет
Пример 2. Кодировка, не поддерживающая символы, приводит к замене символов.
import java.io.*;
import java.util.Arrays;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (OutputStreamWriter osw = new OutputStreamWriter(baos, "ISO-8859-1")) {
osw.write("Привет");
osw.flush();
}
byte[] bytes = baos.toByteArray();
System.out.println(Arrays.toString(bytes));
System.out.println(new String(bytes, "ISO-8859-1"));
[63, 63, 63, 63, 63, 63] ??????
Пример 3. Писатель в стандартный вывод с указанием кодировки.
import java.io.*;
import java.nio.charset.StandardCharsets;
try (OutputStreamWriter osw = new OutputStreamWriter(System.out, StandardCharsets.UTF_8)) {
osw.write("Line 1\nLine 2\n");
osw.flush();
}
Line 1 Line 2
Похожие механизмы в Java и их особенности
- FileWriter - упрощённая обёртка над
OutputStreamWriter, использующая кодировку по умолчанию платформы. Удобен для быстрого кода, но нежелателен при необходимости контроля кодировки. - BufferedWriter - добавляет буферизацию для
Writer, повышая производительность при частых операциях записи. Часто используется вместе сOutputStreamWriter. - PrintWriter - удобные методы форматирования и печати; имеет опцию автосброса. По умолчанию подавляет исключения ввода-вывода, что может скрывать ошибки.
- Files.newBufferedWriter(Path, Charset) (NIO) - современный способ создания писателя с указанием кодировки и опций открытия файла. Предпочтителен для файлового ввода-вывода в новых проектах.
- Channels.newWriter - создаёт
Writerповерх канала, полезен при работе с NIO-каналами.
Выбор зависит от требований: нужен контроль кодировки - использовать OutputStreamWriter или NIO-эквиваленты; требуется буферизация - добавлять BufferedWriter; нужны удобные методы форматирования - применять PrintWriter.
Аналоги в других языках и отличия
PHP
$fp = fopen('php://memory', 'r+');
fwrite($fp, mb_convert_encoding('Привет', 'UTF-8', 'UTF-8'));
rewind($fp);
echo stream_get_contents($fp);
Привет
Отличия: в PHP кодировка управляется внешними функциями, нет встроенного адаптера char->byte как в Java.
JavaScript (Node.js)
const fs = require('fs');
fs.writeFileSync('out.txt', 'Привет', {encoding: 'utf8'});
console.log(Buffer.from('Привет', 'utf8').toString());
Привет
Python
with open('out.txt', 'w', encoding='utf-8') as f:
f.write('Привет')
print('Привет')
Привет
C#
using System.IO;
using System.Text;
var ms = new MemoryStream();
using (var sw = new StreamWriter(ms, Encoding.UTF8, 1024, true)) {
sw.Write("Привет");
sw.Flush();
}
ms.Position = 0;
using (var sr = new StreamReader(ms, Encoding.UTF8)) Console.WriteLine(sr.ReadToEnd());
Привет
Go
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
f, _ := os.Create("out.txt")
w := bufio.NewWriter(f)
fmt.Fprint(w, "Привет")
w.Flush()
}
(файл out.txt содержит "Привет")
Kotlin
import java.io.*
import java.nio.charset.StandardCharsets
val baos = ByteArrayOutputStream()
OutputStreamWriter(baos, StandardCharsets.UTF_8).use { it.write("Привет") }
println(String(baos.toByteArray(), StandardCharsets.UTF_8))
Привет
Lua
local f = io.open('out.txt', 'w')
f:write('Привет')
f:close()
print('Привет')
Привет
SQL
В реляционных СУБД запись текстовых данных в столбцы типа TEXT или NVARCHAR управляется драйвером и настройками соединения; явного аналога адаптера char->byte на стороне базы не требуется. Необходимость кодировки решается параметром соединения и драйвером.
Типичные ошибки при использовании
1. Неправильное имя кодировки
import java.io.*;
OutputStreamWriter osw = new OutputStreamWriter(new ByteArrayOutputStream(), "no-such-charset");
java.io.UnsupportedEncodingException: no-such-charset
2. Запись после закрытия потока
import java.io.*;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(baos);
osw.close();
osw.write('a');
java.io.IOException: Stream closed
3. Непреднамеренная потеря символов при несогласованной буферизации
Если в один и тот же OutputStream одновременно писать байты напрямую и через OutputStreamWriter, возможна интерлейсинга или разрыв многобайтовой последовательности, особенно если порядок записи не контролируется или не выполняется flush() у писателя.
4. Непредставимые символы в выбранной кодировке
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (OutputStreamWriter osw = new OutputStreamWriter(baos, "US-ASCII")) {
osw.write("Привет");
osw.flush();
}
System.out.println(new String(baos.toByteArray(), "US-ASCII"));
??????
5. Подавление исключений при использовании PrintWriter без проверки
При замене OutputStreamWriter на PrintWriter без вызова checkError() часть ошибок записи может быть пропущена.
Изменения и история
Класс OutputStreamWriter присутствует в Java долгое время. Основные моменты истории:
- В ранних версиях использовалась конструкция с именем кодировки. С появлением NIO (Java 1.4) добавлена поддержка
java.nio.charset.Charset, что упростило работу с кодировками и убрало необходимость ловитьUnsupportedEncodingExceptionпри использованииCharset. - В более новых релизах изменений в поведении класса не было много: улучшения касались в основном связанных API (NIO, Files API), благодаря которым появились альтернативы создания писателя.
В последних версиях Java явных изменений контрактов OutputStreamWriter не наблюдается. Рекомендация - использовать API NIO и StandardCharsets для явного указания кодировок.
Расширенные и нетипичные примеры
Пример A. Добавление BOM перед текстом (например, для совместимости с некоторыми редакторами).
import java.io.*;
import java.nio.charset.StandardCharsets;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// Записать UTF-8 BOM вручную
baos.write(new byte[] {(byte)0xEF, (byte)0xBB, (byte)0xBF});
try (OutputStreamWriter osw = new OutputStreamWriter(baos, StandardCharsets.UTF_8)) {
osw.write("Привет\n");
osw.flush();
}
byte[] result = baos.toByteArray();
System.out.println(java.util.Arrays.toString(result));
System.out.println(new String(result, StandardCharsets.UTF_8));
[239, 187, 191, -48, -80, -48, -75, -47, -126, -47, -126, 10] Привет
Пример B. Использование OutputStreamWriter вместе с BufferedWriter для производительности при большом тексте.
import java.io.*;
import java.nio.charset.StandardCharsets;
try (FileOutputStream fos = new FileOutputStream("large.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
BufferedWriter bw = new BufferedWriter(osw, 16 * 1024)) {
for (int i = 0; i < 100_000; i++) {
bw.write("Строка номер " + i + "\n");
}
}
System.out.println("done");
done
Пример C. Работа с сетевым сокетом и явный flush для протоколов с линиями.
import java.net.*;
import java.io.*;
import java.nio.charset.StandardCharsets;
Socket socket = new Socket("example.com", 80);
try (OutputStreamWriter osw = new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8)) {
osw.write("GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n");
osw.flush();
// чтение ответа опущено
}
socket.close();
(HTTP-запрос отправлен на сервер)
Пример D. Управление обработкой ошибок при кодировке через явное кодирование через CharsetEncoder.
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CodingErrorAction;
String text = "Привет €"; // содержит символ €
ByteArrayOutputStream baos = new ByteArrayOutputStream();
CharsetEncoder encoder = Charset.forName("ISO-8859-1").newEncoder()
.onUnmappableCharacter(CodingErrorAction.REPLACE)
.onMalformedInput(CodingErrorAction.REPLACE);
try {
ByteBuffer bb = encoder.encode(CharBuffer.wrap(text));
baos.write(bb.array(), bb.position(), bb.remaining());
} catch (IOException e) {
// не произойдёт при записи в ByteArrayOutputStream
}
System.out.println(new String(baos.toByteArray(), java.nio.charset.Charset.forName("ISO-8859-1")));
????? ?
Пример E. Использование NIO-каналов: создание писателя над каналом через Channels.newWriter.
import java.io.*;
import java.nio.channels.Channels;
import java.nio.charset.StandardCharsets;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
WritableByteChannel channel = Channels.newChannel(baos);
try (Writer w = Channels.newWriter(channel, StandardCharsets.UTF_8)) {
w.write("Текст через канал\n");
}
System.out.println(baos.toString("UTF-8"));
Текст через канал