[TIL] 2022년 9월 21일 수요일
4. 문자기반 스트림
- 문자기반 스트림은 바이트 단위로 데이터를 다루던 바이트기반 스트림 달리 문자 단위로 데이터를 다루는 점을 제외하면 사용 방법은 동일
- 그렇다면 문자기반 스트림과 바이트기반 스트림은 무엇이 다를까?
→ 바이트기반 스트림은 데이터를 읽어올 때 read(byte[] b)을 이용하고 문자기반 스트림은 read(char[] c)를 이용
Stream클래스는 byte 기반 클래스로 byte 단위로 데이터를 읽고 쓰기때문에 이미지처럼 이진수로 된 파일을 다루거나 작은 데이터를 다루는데 유용하다. Reader/Writer 클래스는 문자 기반 클래스로 하나의 문자씩 데이터를 읽고 쓰기 때문에 텍스트 파일이나 다른 텍스트 스트림을 다루는데 유용하다.
InputStream은 데이터를 byte 단위로 읽고 어떤 번역의 과정을 거치지 않는다. 이진 파일이나 이미지 파일을 읽을 때 InputStream을 사용한다. Reader는 문자기반 스트림으로 Input Stream 원본에서 디코딩 과정을 거쳐 유니코드 문자로 돌려준다. 텍스트로의 어떤 유형이든 Reader를 사용한다.
사실 책에 char가 2byte라서 무조건 문자기반 스트림이 2byte 단위로 스트림을 처리하는 것이 아니고 Reader/Writer 클래스와 그 자식 클래스들은 특정 인코딩을 읽어서 유니코드로 변환하고 유니코드를 특정 인코딩으로 변환하여 저장한다는 이야기가 나와서 그걸 찾고싶었는데 찾지 못했다.
한가지 더 보자면 'a‡a'는 우리가 보기에 3글자이지만 ‡가 유니코드 문자라서 InputStream으로 'a‡a'를 읽으면 a는 바이트의 범위에 들지만 '‡'는 8225로 바이트를 초과해 226, 128, 161 총 3 byte가 된다. 하지만 Reader로 읽으면 8227이 되어 97 8227 97이 된다.
4.1 Reader와 Writer
- Reader 클래스와 Writer 클래스 모두 Object 클래스의 자식 클래스로 Buffered, CharArray, Filter, Stream, Piped, String Reader/Writer를 부모 클래스
- Reader 클래스는 URLReader, Writer 클래스는 PrintWriter로 각각 고유한 클래스의 부모 클래스
자료형 | 메서드 | 설명 |
abstract void | close() | Closes the stream and releases any system resources associated with it. |
void | mark(int readAheadLimit) | Marks the present position in the stream. |
boolean | markSupported() | Tells whether this stream supports the mark() operation |
static Reader | nullReader() | Returns a new Reader that reads no characters. |
int | read() | Reads a single character. |
int | read(char[] cbuf) | Reads characters into an array. |
abstract int | read(char[] cbuf, int off, int len) | Reads characters into a portion of an array. |
int | read(CharBuffer target) | Attempts to read characters into the specified character buffer. |
boolean | ready() | Tells whether this stream is ready to be read. |
void | reset() | Resets the stream. |
long | skip(long n) | Skips characters. |
long | transferTo(Writer out) | Reads all characters from this reader and writes the characters to the given writer in the order that they are read. |
자료형 | 메서드 | 설명 |
Writer | append(char c) | Appends the specified character to this writer. |
Writer | append(CharSequence csq) | Appends the specified character sequence to this writer. |
Writer | append(CharSequence csq, int start, int end) | Appends a subsequence of the specified character sequence to this writer. |
abstract void | close() | Closes the stream, flushing it first. |
abstract void | flush() | Flushes the stream. |
static Writer | nullWriter() | Returns a new Writer which discards all characters. |
void | write(char[] cbuf) | Writes an array of characters. |
abstract void | write(char[] cbuf, int off, int len) | Writes a portion of an array of characters. |
void | write(int c) | Writes a single character. |
void | write(String str) | Writes a string. |
void | write(String str, int off, int len) | Writes a portion of a string. |
4.2 FileReader와 FileWriter
- Reader와 Writer 클래스 페이지에 FileReader와 FileWriter가 없어 이상하다 생각했는데 Reader와 Writer의 자식 클래스인InputStreamReader/OutputStreamWriter의 자식 클래스
- 문자 파일을 텍스트로 읽고, 텍스트를 문자 파일로 씀
- byte로 인코딩 할 때는 특정 charset이나 플랫폼 기반 charset을 따름
4.3 PipedReader와 PipedWriter
- 쓰레드 간 데이터를 주고받을 때 사용
- 입력 스트림과 출력스트림을 생성한 다음 둘 중 하나의 스트림에 connect()를 호출해서 하나의 스트림으로 연결하여 데이터를 주고받고, 입출력을 마친 뒤 한쪽 스트림만 닫아도 나머지 스트림이 닫힘
- 생성자를 선언할 때 매개변수로 아무것도 받지 않거나 정수로 pipeSize만 받으면 아직 연결되지 않음을 의미함
import java.io.IOException;
import java.io.PipedReader;
import java.io.PipedWriter;
import java.io.StringWriter;
public class PipedReaderWriter {
public static void main(String[] args) {
InputThread inThread = new InputThread("InputThread");
OutputThread outThread = new OutputThread("OutputThread");
inThread.connect(outThread.getOutput());
inThread.start();
outThread.start();
}
}
class InputThread extends Thread{
PipedReader input = new PipedReader();
StringWriter sw = new StringWriter();
InputThread(String name){
super(name);
}
public void run(){
try{
int data = 0;
while ((data=input.read()) != -1)
sw.write(data);
System.out.println(getName() + " received : " + sw.toString());
}catch (IOException e){}
}
public PipedReader getInput(){
return input;
}
public void connect(PipedWriter output){
try {
input.connect(output);
}catch (IOException e){}
}
}
class OutputThread extends Thread{
PipedWriter output = new PipedWriter();
OutputThread(String name){
super(name);
}
public void run() {
try {
String msg = "Hello!";
System.out.println(getName() + " sent : " + msg);
output.write(msg);
output.close();
}catch (IOException e) { }
}
public PipedWriter getOutput(){
return output;
}
public void connect(PipedReader input){
try {
output.connect(input);
}catch (IOException e) { }
}
}