I/O 기본2
문자 다루기1 - 시작
스트림의 모든 데이터는 byte
를 사용한다. 따라서 byte
가 아닌 문자를 직접 스트림에 전달할 수 없다.
예를 들어, String
문자를 스트림을 통해 파일에 전달하려면 String
을 byte
로 변환한 다음에 저장해야 한다.
byte[] writeBytes = writeString.getBytes(UTF-8)
String
을byte
로 변환할 때는String.getBytes(Charset)
을 사용하면 된다.이때 문자를
byte
숫자로 변경해야 하기 때문에, 반드시 문자 집합(인코딩 셋) 을 지정해야 한다.여기서는 UTF_8로 인코딩 한다.
ABC 를 인코딩하면 65,66,67 이 된다.
이렇게 만든 byte[]
을 FileOutputStream
에 write()
로 전달하면 65,66,67 을 파일에 저장할 수 있다. 결과적으로 우리가 의도한 ABC 문자를 파일에 저장할 수 있다.
String readString = new String(readBytes, UTF_8)
반대의 경우도 비슷하다.
String
객체를 생성할 때, 읽어들인byte[]
과 디코딩 할 문자 집합을 전달하면 된다.그러면
byte[]
을String
문자로 다시 복원할 수 있다.
여기서 핵심은 스트림은 byte
만 사용할 수 있으므로, String
과 같은 문자는 직접 전달할 수 는 없다는 점이다. 그래서 개발자가 번거롭게 다음과 같은 변환 과정을 직접 호출해주어야 한다.
String
+ 문자 집합 ->byte[]
byte[]
+ 문자 집합 ->String
2. 문자 다루기2 - 스트림을 문자로
OutputStreamWriter
: 스트림에 byte 대신에 문자를 저장할 수 있게 지원한다.InputStreamReader
: 스트림에 byte 대신에 문자를 읽을 수 있게 지원한다.
OutputStreamWriter
OutputStreamWriter
는 문자를 입력 받고, 받은 문자를 인코딩해서byte[]
로 변환한다.OutputStreamWriter
는 변환한byte[]
를 전달할OutputStream
과 인코딩 문자 집합에 대한 정보가 필요하다. 따라서 두 정보를 생성자를 통해 전달해야 한다.new OutputStreamWrite(fos, UTF_8)
osw.writer(writeString)
을 보면String
문자를 직접 전달하는 것을 확인할 수 있다.그림을 보면
OutputStreamWriter
가 문자 인코딩을 통해byte[]
로 변환하고, 반환 결과를FileOutputStream
에 전달하는 것을 확인할 수 있다.
InputStreamReader
데이터를 읽을 때는
int ch = read()
를 제공하는데, 여기서는 문자 하나인char
형으로 데이터를 받게 된다. 그런데 실제 반환 타입은int
형이므로char
형으로 캐스팅하여 사용하면 된다.자바의
char
형은 파일의 끝(EOF)인-1
를 표현할 수 없으므로 대신int
를 반환한다.그림을 보면 데이터를 읽을 때,
FileInputStream
에서byte[]
를 읽은 것을 확인할 수 있다.InputStreamReader
는 이렇게 읽은byte[]
을 문자인char
로 변경해서 반환한다. 물론byte
를 문자로 변경할 때도 문자 집합이 필요하다.new InputStreamReader(fis, UTF_8)
OutputStreamWriter
, InputStreamReader
덕분에 매우 편리하게 문자를 byte[]
로 변경하고, 그 반대도 가능하다. 덕분에 개발자는 쉽게 String
문자를 파일에 저장할 수 있다.
앞서 우리가 스트림을 배울 때 분명 byte
단위로 데이터를 읽고 쓰는 것을 확인했다. write()
의 경우에도 byte
단위로 데이터를 읽고 썼다. 최상위 부모인 OutputStream
의 경우 분명 write()
가 byte
단위로 입력하도록 되어있다.
그런데 OutputStreamWriter
의 write()
는 byte
가 아니라 String
이나 char
를 사용한다. 어떻게 된 일일까?
문자 다루기3 - Reader, Writer
자바는 byte 를 다루는 I/O 클래스와 문자를 다루는 I/O 클래스를 둘로 나누었다.
byte 를 다루는 클래스는
OutputStream
,InputStream
의 자식이다.부모 클래스의 기본 기능도
byte
단위를 다룬다.클래스 이름 마지막에 보통
OutputStream
,InputStream
이 붙어있다.
문자를 다루는 클래스는
Writer
,Reader
의 자식이다.부모 클래스의 기본 기능은
String
,char
같은 문자를 다룬다.클래스 이름 마지막에 보통
Writer
,Reader
가 붙어있다.
우리가 앞서 본 OuputStreamWriter
는 바로 문자를 다루는 Wrtier
클래스의 자식이다. 그래서 write(String)
이 가능한 것이다. OutputStreamWriter
는 문자를 받아서 byte 로 변경한 다음에 byte 를 다루는 OutputStream
으로 데이터를 전달했던 것이다.
여기서 꼭! 기억해야 할 중요한 사실이 있다. 처음에 언급했듯이 모든 데이터는 byte 단위(숫자)로 저장된다.
따라서 Writer
가 아무리 문자를 다룬다고 해도 문자를 바로 저장할 수는 없다. 이 클래스에 문자를 전달하면 결과적으로 내부에서는 지정된 문자 집합을 사용해서 문자를 byte 로 인코딩해서 저장한다.
FileWriter, FileReader
Writer
, Reader
를 사용하는 다른 예를 하나 더 보자.
new FileWriter(FILE_NAME, UTF_8)
FileWriter
에 파일명과, 문자 집합(인코딩 셋) 을 전달한다.FileWriter
는 사실 내부에서 스스로FileOutputStream
을 하나 생성해서 사용한다.모든 데이터는 byte 단위로 저장된다는 사실을 다시 떠올려보자
fw.wirte(writeString)
이 메서드를 사용하면 문자를 파일에 직접 쓸 수 있다. (물론 내부적으로 그런 것은 아니다)
이렇게 문자를 쓰면
FileWriter
내부에서는 인코딩 셋을 사용해서 문자를 byte 로 변경하고,FileOutputStream
을 사용해서 파일에 저장한다.개발자가 느끼기에는 문자를 직접 파일에 쓰는 것 처럼 느껴지지만, 실제로는 내부에서 문자를 byte 로 변환한다.
new FileReader(FILE_NAME, UTF_8)
앞서 설명한
FileWriter
와 같은 방식으로 작동한다 .내부에서
FileInputStream
을 생성해서 사용한다.
ch = fe.read()
데이터를 읽을 때도 내부에서는
FileInputStream
을 사용해서 데이터를 byte 단위로 읽어들인다. 그리고 문자 집합을 사용해서byte[]
을char
로 디코딩한다.
FileWriter 와 OutputStreamWriter
FileWriter
코드와 앞서 작성한OutputStreamWriter
를 사용한 코드가 뭔가 비슷하다는 점을 알 수 있다.딱 하나의 차이점이 있다면 이전 코드에서는
FileOutputStream
을 직접 생성했는데,FileWriter
는 생성자 내부에서 대신FileOutputStream
을 생성해준다.사실
FileWriter
는OuputStreamWriter
를 상속한다. 그리고 다른 추가 기능도 없다.딱 하나, 생성자에서 개발자 대신에
FileOutputStream
을 생성해주는 일만 대신 처리해준다.따라서
FileWriter
는OutputStreamWriter
를 조금 편리하게 사용하게 해줄 뿐이다. (FileReader
도 마찬가지..)
정리
Writer
, Reader
클래스를 사용하면 바이트 변환 없이 문자를 직접 다룰 수 있어서 편리하다. 하지만 실제로는 내부에서 byte로 변환해서 저장한다는 점을 꼭 기억하자. 모든 데이터는 바이트 단위로 다룬다! 문자를 직접 저장할 수는 없다!
그리고 반드시 기억하자, 문자를 byte로 변경하려면 항상 문자 집합(인코딩 셋)이 필요하다!
참고: 문자 집합을 생략하면 시스템 기본 문자 집합이 사용된다.
문자 다루기4 - BufferedReader
BufferedOutputStream
, BufferdInputStream
과 같이 Reader
, Writer
에도 버퍼 보조 기능을 제공하는 BufferedReader
, BufferedWriter
클래스가 있다.
추가로 문자를 다룰 때는 한 줄 단위로 다룰 때가 많다.
BufferedReader
는 한 줄 단위로 문자를 읽는 기능도 추가로 제공한다.
br.readLine()
한 줄 단위로 문자를 읽고
String
을 반환한다.파일의 끝(EOF) 에 도달하면
null
을 반환한다.반환 타입이
String
이기 때문에, EOF 를-1
로 표현할 수 없다. 대신에null
을 반환한다.
Last updated