자바에서의 입출력 - 2 (Reader/Writer)
저번 포스팅(자바의 입출력 -1)에서는 바이트 기반의 스트림들을 알아보았습니다. 바이트기반 스트림은 입출력의 단위가 1byte입니다. 하지만 한글과 같은 2 byte형 문자를 표현하기에는 바이트기반 스트림에서 한계가 있었습니다. 이 점을 보완하기 위한 문자기반의 스트림에 대해서 이번 포스팅에서 알아보도록 하겠습니다.
(문자데이터를 다루는데 사용된다는 것을 제외하고는 바이트기반 스트림과 사용방법이 거의 같기 때문에 기본적인 메서드에 대해서만 설명하고 넘어가도록 하겠습니다. 만약 바이트기반 스트림과 보조 스트림에 대해서 생소하시면 자바의 입출력 - 1 포스팅부터 보시는 것을 추천드립니다!)
자유로운 피드백은 환영입니다!!

바이트기반 스트림 VS 문자기반 스트림
바이트기반 스트림과 문자기반 스트림은 이름만 조금 다르고 활용방법은 거의 같습니다.
바이트기반 스트림 | 문자기반 스트림 |
FileInputStream FileOutputStream |
FileReader FileWriter |
ByteArrayInputStream ByteArrayOutputStream |
CharArrayReader CharArrayWriter |
PipedInputStream PipedOutputStream |
PipedReader PipedWriter |
StringBufferedInputStream(deprecated - StringReader 사용) StringBufferedOutputStream(deprecated - StringWriter 사용) |
StringReader StringWriter |
바이트기반 보조스트림 VS 문자기반 보조스트림
바이트기반 보조스트림 | 문자기반 보조스트림 |
BufferedInputStream BufferedOutputStream |
BufferedReader BufferedWriter |
FilterInputStream FilterOutputStream |
FilterReader FilterWriter |
LineNumberInputSTream (deprecated - LineNumberReader 사용) |
LineNumberReader |
PrintStream | PrintWriter |
PushbackInputStream | PushbackReader |
문자기반 스트림
Reader와 Writer
InputStream/OutputStream과 동일한 메서드를 사용하지만, byte배열 대신 char 배열을 사용한다는 것에서 차이가 있습니다.
Reader
public abstract class Reader extends Object implements Readable, Closeable{
// 캐릭터 하나를 read
// char 범위인 0~65535범위의 읽은 정수를 반환
// 입력스트림 마지막에 도달하면 -1을 반환
public int read() throws IOException {
...
}
// 입력소스로부터 매개변수로 주어진 배열 c 크기만큼 읽어서 배열 c에 저장
// 읽어온 데이터 개수를 반환
// 입력스트림의 마지막에 도달하면 -1을 반환
public int read(char[] c) throws IOException{
...
}
// 입력소스로부터 len개의 문자를 읽어서 배열 c의 지정된 위치(off)로 부터 읽은만큼 저장
// 읽어들인 데이터의 개수를 반환
// 입력스트림 마지막에 도달시 -1을 반환
public abstract int read(char[] c, int off, int len) throws IOException{
...
}
// 입력스트림을 닫음으로써 사용하고 있던 자원을 반환
public abstract void close() throws IOException {
...
}
...
}
Writer
public abstract class Writer extends Object implements Appendable, Closeable, Flushable{
// 지정된 문자 c를 출력소스에 출력
public Writer append(char c) throws IOException{
...
}
// 지정된 문자열 c를 출력소스에 출력
public Writer append(CharSequence c) throws IOException{
...
}
// 지정된 문자열 c중 일부(start <= < end)를 출력소스에 출력
public Writer append(CharSequence c, int start, int end) throws IOException{
...
}
// 출력스트림을 닫는다.
public abstract void close() throws IOException{
...
}
// 스트림의 버퍼에 있는 모든 내용을 출력소스에 출력 (버퍼가 있는 스트림에만 해당)
public abstract void flush() throws IOException{
...
}
// 주어진 값(b)를 출력소스에 출력
public void write(int b) throws IOException{
...
}
// 주어진 문자열(str)을 출력소스에 출력
public void write(String str) throws IOException{
...
}
// 주어진 배열 cb에 저장된 모든 내용을 출력소스에 출력
public void write(char[] c) throws IOException{
...
}
// 주어진 배열 c에 저장된 내용 중에서 off번째부터 len 길이만큼만 출력소스에 출력
public abstract void write(char[] c, int off, nt len) hrows IOException{
...
}
// 주어진 문자열 str의 off번째 문자부터 len개만큼의 문자열을 출력소스에 출력
public void write(String str, int off, int len) throws IOException{
...
}
...
}
FileReader와 FileWriter
FileInputStream/FileOutputStream과 사용방법이 동일합니다. 저번 포스팅에서 다루었던 한글이 적혀있는 파일(abc.txt)을 FileReader/FileWriter로 다루어보겠습니다.
- abc.txt 파일
hello!
안녕하세요
소스코드
public class Main {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("abc.txt");
int data = 0;
while ((data = fr.read()) != -1) {
System.out.print((char) data);
}
fr.close();
}
}
출력결과
아래와 같이 제대로 한글을 출력하는 것을 확인할 수 있습니다.
BufferedReader와 BufferedWriter
FileReader와 FileWriter는 FileInputStream/FileOutputStream과 마찬가지로 보조 스트림(BufferedReader/BufferedWriter)를 이용하여 파일 입출력 속도를 크게 향상할 수 있습니다.
BufferedReader에서는 read() 함수도 제공하지만, String readLine() 함수도 추가로 제공해주어서, 데이터를 라인단위로 읽어 올 수 있다는 장점이 있습니다. BufferedWriter에서는 newLine()라는 줄바꿈 메서드를 제공해 줍니다.
BufferedReader
public String readLine() throws IOException {
return readLine(false, null);
}
BufferedWriter
public void newLine() throws IOException {
write(System.lineSeparator());
}
예시코드
public class Main {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("abc.txt");
BufferedReader br = new BufferedReader(fr);
FileWriter fw = new FileWriter("hello.txt");
BufferedWriter bw = new BufferedWriter(fw);
String str = "";
while ((str = br.readLine()) != null) { // readLine()
System.out.println(str);
bw.write(str);
bw.newLine(); // newLine()
}
br.close();
bw.close();
}
}
출력결과
그 외 기반 스트림
PipedReader와 PipedWriter
이들은 쓰레드 간에 데이터를 주고받을 때 사용됩니다. 다른 스트림과 달리 입력과 출력스트림을 하나의 스트림으로 연결(connect)해서 데이터를 주고받는다는 특징이 있습니다.
스트림 생성 후, 어느 한쪽 쓰레드에서 connect()를 호출해서 입력스트림과 출력스트림을 연결합니다. 입출력을 마친 후에는 어느 한쪽 스트림만 닫아도 나머지 스트림은 자동으로 닫힙니다.
StringReader와 StringWriter
CharArrayReader/CharArrayWriter와 같이 입출력 대상이 메모리인 스트림입니다. StringWriter에 출력되는 데이터는 내부의 StringBuffer에 저장되고, StringWriter는 아래와 같은 메서드를 이용해 저장된 데이터를 얻을 수 있습니다.
StringBuffer getBuffer(): StringWriter에 출력한 데이터가 저장된 StringBuffer 반환
String toString(): StringWriter에 출력된 (StringBuffer에 저장된) 문자열 반
PipedReader/Writer와 StringReader/Writer 예시코드
public class Main {
public static void main(String[] args) throws IOException {
InputThread inputThread = new InputThread("InputThread");
OutputThread outputThread = new OutputThread("OutputThread");
inputThread.connect(outputThread.getOutput());
inputThread.start();
outputThread.start();
}
}
class InputThread extends Thread{
PipedReader pr = new PipedReader();
StringWriter sw = new StringWriter();
InputThread(String name){
super(name);
}
public void run(){
try{
int data=0;
while((data=pr.read())!=-1){
sw.write(data);
}
System.out.println(getName() + " received: "+ sw.toString());
// System.out.println(getName() + " received: "+ sw.toString());
// System.out.println(getName() + " received: "+ sw.getBuffer().toString());
} catch (IOException e) {}
}
public PipedReader getInput() {
return pr;
}
public void connect(PipedWriter pw){
try{
pr.connect(pw);
}catch (IOException e) {}
}
}
class OutputThread extends Thread{
PipedWriter pw = new PipedWriter();
OutputThread(String name){
super(name);
}
public void run(){
try{
String msg = "Hello!! 안녕하세요";
System.out.println(getName() + " sent : " + msg);
pw.write(msg);
pw.close();
}catch(IOException e){}
}
public PipedWriter getOutput() {
return pw;
}
public void connect(PipedReader pr){
try{
pr.connect(pw);
}catch (IOException e) {}
}
}
출력결과
그 외 보조 스트림
InputStreamReader와 OutputStreamWriter
이들은 바이트기반 스트림을 문자기반 스트림으로 연결시켜 주는 역할을 합니다. 바이트기반 스트림의 데이터를 지정된 인코딩의 문자데이터로 변환합니다.
예를 들어, 한글윈도우에서 중국어로 된 파일을 읽을 때 인코딩이 중국어로 되어 있다는 것을 지정해 주어야 파일의 내용을 깨지지 않고 제대로 읽고 쓸 수 있습니다.
InputStreamReader
생성자/메서드 | 설명 |
InputStreamReader(InputStream in) | OS에서 사용하는 기본 인코딩의 문자로 변환하는 InputStreamReader 생성합니다. |
InputStreamReader(InputStream in, String encoding) | 지정된 인코딩을 사용하는 InputStreamReader 생성합니다. |
String getEncoding() | InputStreamReader의 인코딩 알려줍니다. |
OutputStreamWriter
생성자/메서드 | 설명 |
OutputStreamWriter (OutputStream in) | OS에서 사용하는 기본 인코딩의 문자로 변환하는 OutputStreamWriter 생성합니다. |
OutputStreamWriter ( OutputStream in, String encoding) | 지정된 인코딩을 사용하는 OutputStreamWriter 생성합니다. |
String getEncoding() | OutputStreamWriter 의 인코딩 알려줍니다. |
예시 코드
( JDK1.5부터 Scanner 클래스로 편리하게 입력받을 수 있어 InputStreamReader를 사용하지 않아도 쉽게 입력받을 수 있습니.)
public class Main {
public static void main(String[] args) {
String line = "";
try {
InputStreamReader isr = new InputStreamReader(System.in); // Scanner 사용 가능
BufferedReader br = new BufferedReader(isr);
System.out.println("사용중인 OS 인코딩: " + isr.getEncoding());
do {
System.out.print("문자 입력: ");
line = br.readLine();
System.out.println("입력 문자: " + line);
} while (!line.equalsIgnoreCase("q"));
} catch (IOException e) {
}
}
}
출력결과
[참고자료]
남궁 성, [Java의 정석 3rd Edition], 도우출판, 2016
https://velog.io/@97ben/Java-%ED%8C%8C%EC%9D%BC-IO-%EC%A0%95%EB%A6%AC
Java 파일 I/O 정리
java의 입출력 I/O에 대해 정리합니다. 자바에서 입출력를 수행하려면 두 대상을 연결하려는 무엇인가가 필요하고 이를 스트림(Stream)이라고 한다. 이때 스트림은 단방향으로 통신이 가능하며 하
velog.io