๐ค ์คํธ๋ฆผ(Stream) ์ด๋?
์คํธ๋ฆผ์ด๋ ๋ฐ์ดํฐ์ ํ๋ฆ์ ์๋ฏธํฉ๋๋ค.
์ดํดํ๊ธฐ ์ฝ๊ฒ ์๋ฅผ ๋ค์ด๋ณด๊ฒ ์ต๋๋ค.
๋ฉ๋ชจ์ฅ์ ๋ฌด์ธ๊ฐ๋ฅผ ๊ธฐ๋กํ๊ธฐ ์ํด ํค๋ณด๋๋ก ์ ๋ ฅ์ ํ๋ ์ํฉ์์๋ ์คํธ๋ฆผ์ด ๋ฐ์ํฉ๋๋ค.
ํค๋ณด๋์์ ๋ฉ๋ชจ์ฅ์ผ๋ก ๋ฐ์ดํฐ๊ฐ ํ๋ฅด๊ธฐ ๋๋ฌธ์ ๋๋ค.
์ด๋ฒ์๋ ์ฝ๋๋ฅผ ํตํด ํ ๊ฐ์ง ์์๋ฅผ ์ดํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
import java.util.Scanner;
public class Main {
private static Scanner sc = new Scanner(System.in);
public static void main(String[] args) {
String input = sc.nextLine();
System.out.println(input);
}
}
์ ์ฝ๋๋ฅผ ์คํํ๊ณ "๋ง๋"์ ์ ๋ ฅํด ๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ ๊ฒฐ๊ณผ๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
์๋ ์คํธ๋ฆผ์ ํ ํํ์ ๋๋ค.
์ฌ์ฉ์์ ํค๋ณด๋์์ ๋ฐ์ดํฐ๊ฐ ์ถ๋ฐํ์ฌ ํ๋ก๊ทธ๋จ์ ๋์ฐฉํ๊ณ , ํด๋น ๋ฐ์ดํฐ๋ ๋ค์ ํ๋ก๊ทธ๋จ์์ ์ถ๋ฐํ์ฌ ํ๋ฉด์ ํ์๋ฉ๋๋ค.
์ด๋ฌํ ์คํธ๋ฆผ์ ํ๋ก๊ทธ๋จ ์ ์ฅ์์ ์๊ฐํ๋ฉด ๋ค์ ๋ ๊ฐ์ง๋ก ๊ตฌ๋ถํ ์ ์์ต๋๋ค.
- ๋ฐ์ดํฐ๊ฐ ์ธ๋ถ๋ก๋ถํฐ ์ถ๋ฐํ์ฌ ํ๋ก๊ทธ๋จ์ผ๋ก ๋ค์ด์ค๋ ์คํธ๋ฆผ
- ๋ฐ์ดํฐ๊ฐ ํ๋ก๊ทธ๋จ์์ ์ถ๋ฐํ์ฌ ํ๋ก๊ทธ๋จ ์ธ๋ถ๋ก ๋น ์ ธ๋๊ฐ๋ ์คํธ๋ฆผ
์ด๋ ์ ์๋ฅผ InputStream, ํ์๋ฅผ OutputStream์ด๋ผ ๋ถ๋ฆ ๋๋ค.
๊ทธ๋ฆผ์ผ๋ก ํํํ๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
์ฝ๊ฒ ๋งํ๋ฉด ํ๋ก๊ทธ๋จ์ InputStream์ ํตํด ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ณ , OutputStream์ ํตํด ๋ฐ์ดํฐ๋ฅผ ์๋๋ค.
์ด๋ ๋ฐ์ดํฐ๋ ๋ฐ์ดํธ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ฝ๊ณ ์ฐ์ฌ์ง๊ฒ ๋ฉ๋๋ค.
๐ค ์๋ฐ์ OutputSteam ์ ๋ํด ์ดํด๋ณด์
์๋ฐ์์๋ OutputStream์ ํตํด ๋ฐ์ดํฐ ์ถ๋ ฅ์ ์ฒ๋ฆฌํฉ๋๋ค.
์ด๋ ๊ธฐ๋ฐ์ด ๋๋ ๋ฉ์๋๋ write(int b) ์ ๋๋ค.
b๋ ๋ฐ์ดํธ์ ํฌ๊ธฐ๋ฅผ ์ง์ ํ๋ ํ๋ผ๋ฏธํฐ์ ๋๋ค.
ํ๋ฒ ์์๋ฅผ ์ดํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
@Test
void OutputStream์_๋ฐ์ดํฐ๋ฅผ_๋ฐ์ดํธ๋ก_์ฒ๋ฆฌํ๋ค() throws IOException {
byte[] bytes = {110, 101, 120, 116, 115, 116, 101, 112};
OutputStream outputStream = new ByteArrayOutputStream(bytes.length);
outputStream.write(bytes);
String actual = outputStream.toString();
assertThat(actual).isEqualTo("nextstep");
outputStream.close();
}
(110, 101, 120, 116, 115, 116, 101, 112๋ฅผ ์์คํค์ฝ๋์ ๋ง์ถฐ ๋ฌธ์๋ก ๋์์์ผ๋ณด๋ฉด nextstep์ ๋๋ค.)
OutputStream์ ๋ชจ๋ ์ฌ์ฉํ์๋ค๋ฉด ๋ฐ๋์ close()๋ฅผ ํธ์ถํด ์ฃผ์ด์ผ ํฉ๋๋ค.
(OutputStream๋ Closable์ ๊ตฌํํ์๊ธฐ์ try with resource๋ฅผ ํตํด์ ๊ฐํธํ๊ฒ close() ์ํฌ ์ ์์ต๋๋ค)
๐ณ BufferedOutputStream์ ํตํด ๋ฒํผ๋ง์ ์ฌ์ฉํ์
๋ฐ์ดํฐ์ ํจ์จ์ ์ธ ์ ์ก์ ์ํด ๋ฒํผ๋ง์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
์๋ฅผ ๋ค์ด ํ๋๋์คํฌ์ ์ด๋ค ํ์ผ์ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด์ผ ํ๋ ๊ฒฝ์ฐ๋ฅผ ์๊ฐํด ๋ณด๊ฒ ์ต๋๋ค.
ํ๋๋์คํฌ์ ์ ๊ทผํ๋ ๊ฒ์ ๋งค์ฐ ๋๋ฆฌ๊ธฐ ๋๋ฌธ์ write() ๋ฉ์๋๋ฅผ ํตํด ๋ฐ์ดํธ ๋จ์๋ก ํ์ผ์ ์์ฑํ๊ฒ ๋๋ค๋ฉด ์ฑ๋ฅ์ด ๋งค์ฐ ๋๋ฆด ๊ฒ์ด ๋น์ฐํฉ๋๋ค.
์ด๋ด ๋ ๋ฒํผ๋ง์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
๋ฐ์ดํฐ๋ฅผ ๋ฐ๋ก ํ์ผ์ ์์ฑํ์ง ์๊ณ ๋ฒํผ์ ๋ชจ์๋๋ค๊ฐ ํ๋ฒ์ ์์ฑํ๋ฉด ํ์ผ์ ๋ํ ์ ๊ทผ์ ์ต์ํํ ์ ์์ด ์ฑ๋ฅ์ ์ต์ ํํ ์ ์์ต๋๋ค.
์๋ฐ์์๋ BuffredOutputStream ํด๋์ค๋ฅผ ํตํด OutputStream์์ ๋ฒํผ๋ง์ ์ฌ์ฉํ ์ ์๋๋ก ํด์ค๋๋ค.
@Test
void BufferedOutputStream์_์ฌ์ฉํ๋ฉด_๋ฒํผ๋ง์ด_๊ฐ๋ฅํ๋ค() throws IOException {
byte[] bytes = {110, 101, 120, 116, 115, 116, 101, 112};
OutputStream outputStream = new ByteArrayOutputStream(bytes.length);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
bufferedOutputStream.write(bytes);
bufferedOutputStream.flush(); // ํ์
String actual = outputStream.toString();
outputStream.close();
assertThat(actual).isEqualTo("nextstep");
}
์์ ๊ฐ์ด BuffredOutputStream์ ์์ฑ์์ OutputStream์ ๊ฑด๋ค์ฃผ๋ฉด, ํด๋น OutputStream์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ฐ๋ ๊ฒ์ ๋์ผํ๋ ๋ฒํผ๋ง๋์ด ์ฐ์ฌ์ง๋ค๋ ์ ์ด ์ถ๊ฐ๋ฉ๋๋ค.
์ด๋ ๋ฒํผ๋ง๋ ๋ฐ์ดํฐ๋ฅผ ์ค์ OutputStream์ผ๋ก ์ฐ๊ธฐ ์ํด์๋ flush()๋ฅผ ํธ์ถํด ์ฃผ์ด์ผ ํฉ๋๋ค.
๐ก BuffredOutputStream์ ๊ฐ์ด ๋ค๋ฅธ ์คํธ๋ฆผ๊ณผ ์ฐ๊ฒฐ๋์ด ์ฌ๋ฌ ๊ธฐ๋ฅ๋ค์ ์ถ๊ฐ๋ก ์ ๊ณตํ๋ ์คํธ๋ฆผ์ FilterOutputStream์ด๋ผ ํฉ๋๋ค.
์ด์ ๋ํด์๋ ์กฐ๊ธ ๋ค์ ์ดํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
๐ณ BufferedOutputStream ์ฑ๋ฅ ๋น๊ต
OutputStream: System.out
write() ํธ์ถ ํ์: 1000000 ๋ฒ
write()๋น ์ฐ์ฌ์ง๋ ๋ฐ์ดํฐ: "mallang\n"
๐ ๋ฒํผ๋ง ์๋ ๊ฒฝ์ฐ
public class Main {
public static void main(String[] args) throws IOException {
OutputStream outputStream = System.out;
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
outputStream.write("mallang\n".getBytes());
}
outputStream.close();
long end = System.currentTimeMillis();
System.out.println("๊ฒฝ๊ณผ์๊ฐ: " + (end - start) + "mx");
}
}
๐ ๋ฒํผ๋ง ์ฌ์ฉํ ๊ฒฝ์ฐ
public class Main {
public static void main(String[] args) throws IOException {
OutputStream outputStream = System.out;
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
bufferedOutputStream.write("mallang\n".getBytes());
}
bufferedOutputStream.flush();
long end = System.currentTimeMillis();
System.out.println("๊ฒฝ๊ณผ์๊ฐ: " + (end - start) + "mx");
}
}
์์ฒญ๋ ์ฑ๋ฅ ์ฐจ์ด๊ฐ ์๋ ๊ฒ์ ์ ์ ์์ต๋๋ค.
๐ค ํํฐ ์คํธ๋ฆผ(FilterStream) ์ด๋?
๊ธฐ์กด ์คํธ๋ฆผ์ ์ถ๊ฐ์ ์ผ๋ก ์ฐ๊ฒฐ๋์ด ๋์ค์ ๋ฐ์ดํฐ๋ฅผ ๋ณํํ๊ฑฐ๋ ํน์ ๋ฒํผ๋ง ๋ฑ์ ์ถ๊ฐ ๊ธฐ๋ฅ์ ์ ๊ณตํด์ฃผ๋ ์คํธ๋ฆผ์ ์๋ฏธํฉ๋๋ค.
์๋ฐ์์ ํํฐ ์คํธ๋ฆผ์ ์ต์์ ํด๋์ค๋ FilterInputStream๊ณผ FilterOutputStream์ ๋๋ค.
๊ฐ๊ฐ์ ์ค๋ช ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
FilterInputStream
A FilterInputStream contains some other input stream, which it uses as its basic source of data, possibly transforming the data along the way or providing additional functionality.
FilterOutputStream
This class is the superclass of all classes that filter output streams. These streams sit on top of an already existing output stream (the underlying output stream) which it uses as its basic sink of data, but possibly transforming the data along the way or providing additional functionality.
๐ค ์๋ฐ์ InputSteam ์ ๋ํด ์ดํด๋ณด์
์๋ฐ์์๋ InputStream์ ํตํด ๋ฐ์ดํฐ ์ถ๋ ฅ์ ์ฒ๋ฆฌํฉ๋๋ค.
์ด๋ ๊ธฐ๋ฐ์ด ๋๋ ๋ฉ์๋๋ read() ์ ๋๋ค.
์ค๋ช ์ ์ดํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
Reads the next byte of data from the input stream.
The value byte is returned as an int in the range 0 to 255.
If no byte is available because the end of the stream has been reached, the value -1 is returned.
This method blocks until input data is available, the end of the stream is detected, or an exception is thrown.
read() ๋ฉ์๋์ ๋ฐํ ํ์ ์ int์ด๋ฉฐ, ์ฝ์ ๋ฐ์ดํธ๊ฐ int๋ก ๋ณํ๋์ด ๋ฐํ๋๋ค๊ณ ํฉ๋๋ค. (๋ฒ์๋ 0 ~ 255 ์ฌ์ด์ ๋๋ค)
๊ทธ๋ฆฌ๊ณ ๋์ด์ ์ฝ์ ๋ฐ์ดํฐ๊ฐ ์๋ค๋ฉด -1์ ๋ฐํํฉ๋๋ค.
ํ๋ฒ ์์๋ฅผ ์ดํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
@Test
void InputStream์_๋ฐ์ดํฐ๋ฅผ_๋ฐ์ดํธ๋ก_์ฝ๋๋ค() throws IOException {
byte[] bytes = {-16, -97, -92, -87};
final InputStream inputStream = new ByteArrayInputStream(bytes);
byte[] actual = inputStream.readAllBytes();
assertThat(new String(actual)).isEqualTo("๐คฉ");
assertThat(inputStream.read()).isEqualTo(-1);
inputStream.close();
}
์์ ๊ฐ์ด readAllBytes() ๋ฅผ ํตํด์ InputStream์ ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด์ฌ ์ ์์ต๋๋ค.
InputStream์ ๋ชจ๋ ์ฌ์ฉํ์๋ค๋ฉด ๋ฐ๋์ close()๋ฅผ ํธ์ถํด ์ฃผ์ด์ผ ํฉ๋๋ค.
(InputStream๋ Closable์ ๊ตฌํํ์๊ธฐ์ try with resource๋ฅผ ํตํด์ ๊ฐํธํ๊ฒ close() ์ํฌ ์ ์์ต๋๋ค)
๐ก ์ด๋ชจ์ง๋ฅผ ํํํ๋ ๋ฐฉ๋ฒ: UTF-16 ์ธ์ฝ๋ฉ์ Surrogate Pair
InputStream์๋ ๋ฒํผ๋ง์ ์ ์ฉ์ํฌ ์ ์๋ BufferedInputStream์ด ์กด์ฌํฉ๋๋ค.
@Test
void ํํฐ์ธ_BufferedInputStream๋ฅผ_์ฌ์ฉํด๋ณด์() throws IOException {
final String text = "ํํฐ์ ์ฐ๊ฒฐํด๋ณด์.";
final InputStream inputStream = new ByteArrayInputStream(text.getBytes());
final InputStream bufferedInputStream = new BufferedInputStream(inputStream);
final byte[] actual = bufferedInputStream.readAllBytes();
assertThat(bufferedInputStream).isInstanceOf(FilterInputStream.class);
assertThat(actual).isEqualTo("ํํฐ์ ์ฐ๊ฒฐํด๋ณด์.".getBytes());
}
์์ ๊ฐ์ด ์ฌ์ฉํ ์ ์์ต๋๋ค.
๐ค InputStreamReader์ OutputStreamWriter
InputStream๊ณผ OutputStream์์ ๋ฐ์ดํฐ๋ ๋ฐ์ดํธ ๋จ์๋ก ํ๋ฌ๊ฐ๋๋ค.
๊ทธ๋์ ์ด๋ค์ ๋ฌธ์๋ก ์ฝ๊ฑฐ๋ ์ฐ๊ณ ์ถ์ผ๋ฉด ์ด๋ฅผ ๋ณํํ๋ ๊ณผ์ ์ด ํ์ํฉ๋๋ค.
์๋ฐ๋ InputStreamReader์ OutputStreamWriter๋ฅผ ํตํด byte ์คํธ๋ฆผ์ ๋ฌธ์ ์คํธ๋ฆผ์ผ๋ก ๋ณํํด์ฃผ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
InputStreamReader
An InputStreamReader is a bridge from byte streams to character streams: It reads bytes and decodes them into characters using a specified charset.
The charset that it uses may be specified by name or may be given explicitly, or the platform's default charset may be accepted.
Each invocation of one of an InputStreamReader's read() methods may cause one or more bytes to be read from the underlying byte-input stream.
To enable the efficient conversion of bytes to characters, more bytes may be read ahead from the underlying stream than are necessary to satisfy the current read operation.
For top efficiency, consider wrapping an InputStreamReader within a BufferedReader.
For example:
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
OutputStreamWriter
An OutputStreamWriter is a bridge from character streams to byte streams: Characters written to it are encoded into bytes using a specified charset.
The charset that it uses may be specified by name or may be given explicitly, or the platform's default charset may be accepted.
Each invocation of a write() method causes the encoding converter to be invoked on the given character(s).
The resulting bytes are accumulated in a buffer before being written to the underlying output stream.
Note that the characters passed to the write() methods are not buffered.
For top efficiency, consider wrapping an OutputStreamWriter within a BufferedWriter so as to avoid frequent converter invocations.
For example:
Writer out = new BufferedWriter(new OutputStreamWriter(System.out));
A surrogate pair is a character represented by a sequence of two char values:
A high surrogate in the range '\uD800' to '\uDBFF' followed by a low surrogate in the range '\uDC00' to '\uDFFF'.
A malformed surrogate element is a high surrogate that is not followed by a low surrogate or a low surrogate that is not preceded by a high surrogate.
This class always replaces malformed surrogate elements and unmappable character sequences with the charset's default substitution sequence.
The CharsetEncoder class should be used when more control over the encoding process is required.
Reader์ Writer์์๋ BuffredReader์ BufferedWriter๋ฅผ ํตํด ๋ฒํผ๋ง์ ์ฌ์ฉํ์ฌ ์ฑ๋ฅ์ ํฅ์์ํฌ ์ ์๋ค๊ณ ๋์์์ต๋๋ค.
์ฌ์ฉ ์์๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
@Test
void BufferedReader๋ฅผ_์ฌ์ฉํ์ฌ_๋ฌธ์์ด์_์ฝ์ด์จ๋ค() {
String emoji = String.join("\r\n",
"๐๐๐๐๐๐
๐๐คฃ๐ฅฒโบ๏ธ๐",
"๐๐๐๐๐๐๐ฅฐ๐๐๐๐",
"๐๐๐๐๐คช๐คจ๐ง๐ค๐๐ฅธ๐คฉ",
"");
InputStream inputStream = new ByteArrayInputStream(emoji.getBytes());
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
String actual = br.lines()
.collect(Collectors.joining("\r\n", "", "\r\n"));
assertThat(actual).hasToString(emoji);
}
์ด๋ ๊ฒ ํด์ ์คํธ๋ฆผ์ ๋ํด ๊ฐ๋จํ ๊ณต๋ถํด๋ณด๋ ์๊ฐ์ ๊ฐ์ก์ต๋๋ค.
๋ค์ ๊ธ์์๋ ์ด์ ๊ธ์์ ์ดํด๋ณธ ๋๋จธ์ง ์๋ฌธ์ ์ธ resources ๋๋ ํ ๋ฆฌ ๋ด์ ํ์ผ๋ค์ ์ฐพ๋ ๋ฐฉ๋ฒ์ ๋ํด ์ดํด๋ณด๊ฒ ์ต๋๋ค.