[Java][I/O] 문자기반 스트림 Reader와 Writer

2022. 6. 23. 19:01JAVA/Language

1. Reader와 Writer

바이트기반 스트림의 조상이 InputStream / OutputStream인 것과 같이 문자기반의 스트림에서는 Reader / Writer가 같은 역할을 수행합니다. Reader / Writer는 byte 배열 대신 char 배열을 사용합니다.

 

Reader의 메서드

메서드 설명
abstract void close() 입력스트림을 닫음으로써 사용하고 있던 자원을 반환
void mark(int readlimit) 현재위치를 표시해놓는다. 후에 reset()에 의해서 표시해 놓은 위치로 다시 돌아갈 수 있음
boolean markSupported() mark()reset()을 지원하는지를 알려줌
int read() 입력소스로부터 하나의 문자를 읽어온다. char의 범위인 0~65535 범위의 정수를 반환하며, 입력스트림의 마지막 데이터에 도달하면, -1을 반환함
int read(char[] c) 입력소스로부터 매개변수로 주어진 배열 c의 크기만큼 읽어서 배열 c에 저장한다. 읽어 온 데이터의 개수 또는 -1을 반환함
abstract int read(char[] c, int off, int len) 입력소스로부터 최대 len개의 문자를 읽어서, 배열 c의 지정된 위치(off)부터 읽은 만큼 저장합니다. 읽어온 데이터의 개수 또는 -1을 반환함
int read(CharBuffer target) 입력소스로부터 읽어서 문자버퍼(target)에 저장함
boolean ready() 입력소스로부터 데이터를 읽을 준비가 되어있는지 알려줌
void reset() 입력소스에서의 위치를 마지막으로 mark()가 호출되었떤 위치로 되돌림
long skip(long n) 현재 위치에서 주어진 문자 수(n)만큼 건너뜀

 

Writer의 메서드

메서드 설명
Writer append(char c) 지정된 문자를 출력소스에 출력함
Writer append(CharSequence c) 지정된 문자열(CharSequence)을 출력소스에 출력함
Writer append(CharSequence c, int start, int end) 지정된 문자열(CharSequence)의 일부를 출력소스에 출력(CharBuffer, String, StringBufferCharSequence를 구현)
abstract void close() 출력스트림을 닫음으로써 사용하고 있던 자원을 반환함
abstract void flush() 스트림의 버퍼에 있는 모든 내용을 출력소스에 씀 (버퍼가 있는 스트림에만 해당됨)
void write(int b) 주어진 값을 출력 소스에 씀
void write(char[] c) 주어진 배열 c에 저장된 모든 내용을 출력소스에 씀
abstract void write(char[] c, int off, int len) 주어진 배열 c에 저장된 내용 중에서 off번째부터 len길이만큼만 출력소스에 쓴다.
void write(String str) 주어진 문자열(str)을 출력소스에 쓴다.
void write(String str, int off, int len) 주어진 문자열(str)의 일부를 출력소스에 쓴다. (off번째 문자부터 len개만큼의 문자열)

2. FileReader와 FileWriter

FileReader / FileWriter는 파일로부터 텍스트 데이터를 읽고, 파일에 쓰는데 사용됩니다.

	/**
	 * title : FileReader.read() 메서드를 사용하여 파일 읽기
	 * FileInputStream은 바이트기반 스트림이기 때문에 파일의 내용들을 1byte씩 읽게되어 한글(2byte)을
	 * 읽게될시 깨지게됩니다.
	 * 
	 * FileReader 인스턴스 메서드
	 * int read() : 입력소스로부터 하나의 문자를 읽어옵니다. char의 범위인 0~65535 범위의
	 * 정수를 반환하며, 입력 스트림의 마지막에 도달하면 -1을 반환합니다.
	 */
	@Order(1)
	@Test
	void fileReaderTest() {
		String fileName = "./src/ch15/ex_07_FileReader_FileWriter/test.txt";
		try(FileInputStream fis = new FileInputStream(fileName);
			FileReader fr = new FileReader(fileName)){
			
			int data = 0;
			
			// FileInputStream을 이용해서 파일 내용을 읽어 화면에 출력한다.
			while((data = fis.read()) != -1) { // 한글은 2byte인데 1byte씩 읽어서 깨지게됨
				System.out.print((char) data);
			}
			System.out.println();
			fis.close();
			
			// FileReader을 이용해서 파일 내용을 읽어 화면에 출력한다.
			while((data = fr.read()) != -1) {
				System.out.print((char) data);
			}
			System.out.println();
			fr.close();
		}catch (IOException e) {
			e.printStackTrace();
		}
	}
packagech15.ex_07_FileReader_FileWriter;importjava.io.FileInputStream;importjava.io.FileNotFoundException;importjava.io.FileReader;importjava.io.FileWriter;importjava.io.IOException;importorg.junit.jupiter.api.Order;importorg.junit.jupiter.api.Test;publicclassFileReaderWriterTest{/***title:FileReader.read()메서드를사용하여파일읽기*FileInputStream은바이트기반스트림이기때문에파일의내용들을1byte씩읽게되어한글(2byte)을*읽게될시깨지게됩니다.**FileReader인스턴스메서드*intread():입력소스로부터하나의문자를읽어옵니다.char의범위인0~65535범위의*정수를반환하며,입력스트림의마지막에도달하면-1을반환합니다.*/@Order(1)@TestvoidfileReaderTest(){StringfileName="./src/ch15/ex_07_FileReader_FileWriter/test.txt";try(FileInputStreamfis=newFileInputStream(fileName);FileReaderfr=newFileReader(fileName)){intdata=0;//FileInputStream을이용해서파일내용을읽어화면에출력한다.while((data=fis.read())!=-1){//한글은2byte인데1byte씩읽어서깨지게됨System.out.print((char)data);}System.out.println();fis.close();//FileReader을이용해서파일내용을읽어화면에출력한다.while((data=fr.read())!=-1){System.out.print((char)data);}System.out.println();fr.close();}catch(IOExceptione){e.printStackTrace();}}@Order(2)@TestvoidfileConversionTest(){try(FileReaderfr=newFileReader("./src/ch15/ex_07_FileReader_FileWriter/FileReaderWriterTest.java");FileWriterfw=newFileWriter("./src/ch15/ex_07_FileReader_FileWriter/convert.txt")){intdata=0;while((data=fr.read())!=-1){if(data!='\t'&&data!='\n'&&data!=''&&data!='\r'){fw.write(data);}}}catch(FileNotFoundExceptione){e.printStackTrace();}catch(IOExceptione){e.printStackTrace();}}}

 

3. PipedReader와 PipedWriter

PipedReader / PipedWriter는 쓰레드 간에 데이터를 주고받을 때 사용됩니다. 다른 스트림과는 달리 입력과 출력 스트림을 하나의 스트림으로 연결(connect)해서 데이터를 주고받는다는 특징이 있습니다.

 

스트림을 생성한 다음에 어느 한쪽 스레드에서 connect()를 호출해서 입력스트림과 출력스트림을 연결합니다. 입출력을 마친 후에는 어느 한쪽 스트림만 닫아도 나머지 스트림은 자동으로 닫힙니다.

 

class InputThread extends Thread{
	PipedReader input = new PipedReader();
	StringWriter sw   = new StringWriter();
	
	public 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();
	
	public 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) {

		}
	}
}

public class PipedReaderWriterTest {
	/**
	 * title : PipedReader, PipedWriter 수행 테스트
	 * 입력과 출력스트림을 하나의 스트림으로 연결해서 데이터를 주고받음
	 * 입출력을 마친 후에는 어느 한쪽 스트림만 닫아도 나머지 스트림은 자동으로 닫힘
	 */
	@Order(1)
	@Test
	void PipedReaderWriterTest() {
		InputThread inThread   = new InputThread("InputThread");
		OutputThread outThread = new OutputThread("OutputThread");
		
		// PipedReader와 PipedWriter를 연결한다.
		inThread.connect(outThread.getOutput());
		
		inThread.start();
		outThread.start();
	}
}
OutputThread sent : Hello
InputThread received : Hello

 

4. StringReader와 StringWriter

StringReader / StringWriter는 CharArrayReader / CharArrayWriter와 같이 입출력 대상이 메모리인 스트림입니다. StringWriter에 출력되는 데이터는 내부의 StringnBuffer에 저장되며 StringWriter의 다음과 같은 메서드를 이용해서 저장된 데이터를 얻을 수 있습니다.

StringBuffer getBuffer() : StringWriter에 출력한 데이터가 저장된 StringBuffer를 반환
String       toString()  : StringWriter에 출력된(StringBuffer에 저장된) 문자열을 반환

근본적으로 String도 char배열이지만, 아무래도 char배열보다는 String으로 처리하는것이 여러모로 편리한 경우가 많을 것입니다.

	@Order(1)
	@Test
	void StringReaderWriterTest() {
		String inputData = "ABCD";
		StringReader input  = new StringReader(inputData);
		StringWriter output = new StringWriter();
		
		int data = 0;
		
		try {
			while((data = input.read()) != -1) {
				output.write(data);
			}
		}catch (IOException e) {
			e.printStackTrace();
		}
		
		System.out.println("Input Data  : " + inputData);
		System.out.println("Output Data : " + output.toString());
		
	}
Input Data  : ABCD
Output Data : ABCD

 

References

source code : https://github.com/yonghwankim-dev/java_study/tree/main/ch15
[도서] Java의 정석, 남궁 성 지음