프로그래밍/JAVA

15-5 보조 스트림

어서오시우 2023. 4. 6. 02:23
728x90

보조 스트림

보조 스트림은 입출력 대상이 되는 파일이나 네트워크에 직접 쓰거나 읽는 기능은 없고 보조 기능을 추가하는 스트림이다.

어떤 보조 스트림이 더해지느냐에 따라 스트림 기능이 추가된다.

 

보조 스트림은 Wrapper 스트림이라고도 한다.

다른 스트림을 감싸고 있다는 의미로, 스스로는 입출력 기능이 없기 때문에 생성자의 매개변수로 다른 스트림을 받게 되면

자신이 감싸고 있는 스트림이 읽거나 쓰는 기능을 수행할 때 보조 기능을 추가한다.

 

 

FileInputStream과 FileOutputStream

FileInputStream과 FileOutputStream은 보조 스트림의 상위 클래스로, 모든 보조 스트림은 둘 중 하나를 상속받게 된다.

또한 앞에서 설명했듯 보조 스트림은 자료 입출력을 직접 할 수 없기 때문에 다른 기반 스트림을 포함한다.

FileInputStream과 FileOutputStream의 생성자는 다음과 같다.

생성자 설명
protected FileInputStream( InputStream in ) 생성자의 매개변수로 InputStream을 받는다.
protected FileOutputStream( OutputStream out ) 생성자의 매개변수로 OutputStream을 받는다.

두 클래스 모두 다른 생성자는 제공하지 않는다.

따라서 이들 클래스를 상속받은 보조 클래스도 상위 클래스에 디폴트 생성자가 없으므로

다른 스트림을 매개변수로 받아 상위 클래스를 호출해야 한다.

 

보조 스트림의 생성자에 항상 기반 스트림만 매개변수로 전달되는 것이 아니며,

때로는 또 다른 보조 스트림을 매개변수로 전달받을 수도 있다는 것을 기억하자.

이때 전달되는 또 다른 보조 스트림은 내부적으로 기반 스트림을 포함하고 있다.

 

 

InputStreamReader와 OutputStreamWriter

바이트 단위로 자료를 읽으면 한글 같은 문자는 깨지기 때문에

문자는 Reader나 Writer에서 상속받은 스트림을 사용해서 자료를 읽거나 써야 한다.

 

하지만 표준 입출력 System.in과 같이 바이트 자료만 입력되는 스트림도 있다.

네트워크에서 소켓, 인터넷이 연결되었을 때 읽거나 쓰는 바이트 단위인 InputStream과 OutputStream도 마찬가지이다.

이렇게 생성된 바이트 스트림을 문자로 변환해 주는 보조 스트림이 InputStreamReader와 OutputStreamWriter이다.

 

보조 스트림은 입출력 기능이 없으므로 다른 입출력 스트림을 포함한다.

InputStreamReader의 생성자를 살펴보면 다음과 같다.

생성자 설명
InputStreamReader( InputStream in ) InputStream 클래스를 생성자의 매개변수로 받아
Reader를 생성한다.
InputStreamReader( InputStream in, Charset cs ) InputStream과 Charset 클래스를 매개변수로 받아
Reader를 생성한다.
InputStreamReader( InputStream in, CharsetDecoder dec ) InputStream과 CharsetDecoder를 매개변수로 받아
Reader를 생성한다.
InputStreamReader( InputStream in, String charsetName ) InputStream과 String으로 문자 세트 이름을 받아
Reader를 생성한다.

InputStreamReader 생성자의 매개변수로 바이트 스트림과 문자 세트를 매개변수로 지정할 수 있다.

 

* 문자 세트는 각 문자가 가지는 고유 값이 어떤 값으로 이루어졌는가에 따라 다르다.

대표적으로 자바에서 사용하는 UTF-16 문자 세트가 있는데 이는 유니코드를 나타내는 문자 세트 중 하나이다.

 

문자 세트란 문자를 표현하는 인코딩 방식이다.

바이트 자료가 문자로 변환될 때 지정된 문자 세트가 적용된다.

적용할 문자 세트를 명시하지 않으면 시스템이 기본으로 사용하는 문자 세트가 적용된다.

 

InputStreamReader의 모든 생성자는 InputStream, 즉 바이트 단위로 읽어 들이는 스트림을 매개변수로 받는다.

생성자에서 매개변수로 받은 InputStream이 자료를 읽으면 InputStreamReader가 읽은 바이트 자료를 문자료 변환해준다.

InputStream인 FileInputStream을 사용하여 InputStreamReader가 해주는 문자 변환 기능을 살펴보자.

 

예제01_InputStreamReader 사용하기

 

InputStreamReader가 FileInputStream을 매개변수로 받아 생성되었다.

또한 12행에서 파일의 끝 -1이 반환될 때까지 보조 스트림으로 읽어 들인다.

FileInputStream은 바이트 단위로 자료를 읽기 때문에 reader.txt에 쓰여 있는 한글 '안녕하세요'를 읽을 수 없다.

InputStreamReader는 파일 스트림이 바이트 단위로 읽어 들인 내용을 문자로 변환해 주는 역할을 한다.

 

파일에서 문자를 읽는 경우는 FileReader로 바로 읽을 수 있으며

위 예제는 InputStream을 쓰기 위해 FileInputStream을 사용한 것이다.

 

표준 입출력 스트림 System.in과 System.out은 모두 바이트 스트림이다.

특히 System.in은 콘솔 화면에서 한글을 읽으려면 InputStreamReader를 사용해야 한다.

Scanner 클래스는 이런 변환이 필요 없어 콘솔 입력에 많이 사용된다.

 

네트워크에서 쓰이는 클래스는 스트림을 생성하려면 InputStream이나 OutputStream으로 생성된다.

이 경우 읽어 들인 자료를 InputStreamReader나 OutputStreamWriter를 활용해 문자로 변환하여 사용한다.

 

 

Buffered 스트림

입출력이 한 바이트나 문자 단위로 이루어지면 그만큼 프로그램 수행 속도가 느려진다.

Buffered 스트림은 내부적으로 8,192 바이트 크기의 배열을 가지고 있으며

이미 생성된 스트림에 배열 기능을 추가해 더 빠르게 입출력을 실행할 수 있는 버퍼링 기능을 제공한다.

한 바이트나 한 문자 단위로 처리할 때보다 훨씬 빠르게 처리 가능하다.

버퍼링 기능을 제공하는 스트림 클래스는 다음과 같다.

스트림 클래스 설명
BufferedInputStream 바이트 단위로 읽는 스트림에 버퍼링 기능을 제공한다.
BufferedOutputStream 바이트 단위로 출력하는 스트림에 버퍼링 기능을 제공한다.
BufferedReader 문자 단위로 읽는 스트림에 버퍼링 기능을 제공한다.
BufferedWriter 문자 단위로 출력하는 스트림에 버퍼링 기능을 제공한다.

버퍼링 기능을 제공하는 스트림 역시 보조 스트림으로 다른 스트림을 포함하여 수행된다.

BufferedInputStream의 생성자를 살펴보면 다음과 같다.

생성자 설명
BufferedInputStream( InputStream in ) InputStream 클래스를 생성자의 매개변수로 받아
BufferedInputStream을 생성한다.
BufferedInputStream( InputStream in, int size ) InputStream 클래스와 버퍼 크기를 생성자의 매개변수로 받아
BufferedInputStream을 생성한다.

BufferedInputSTream은 보조 스트림이므로 생성자의 매개변수로 다른 InputStream을 가져야 한다.

BufferedOutputStream은 OutputStream을, BufferedReader는 Reader를,

BufferedWriter는 Writer 클래스를 생성자의 매개변수로 받는다.

Buffered 스트림이 포함할 스트림이 입력 / 출력 스트림, 문자용 / 바이트용 스트림인지에 따라 알맞은 스트림을 사용한다.

 

 

DataInputStream과 DataOutputStream

지금까지 살펴본 스트림은 사람이 읽고 쓰는 텍스트 형식의 자료를 다루었다.

DataInputStream과 DataOutputStream은 메모리에 저장된 0, 1 상태를 그대로 읽거나 쓰기 때문에 자료형의 크기가

그대로 보존된다. 두 스트림은 다음과 같은 생성자를 제공한다.

생성자 설명
DataInputStream( InputStream in ) InputStream을 생성자의 매개변수로 받아
DataInputStream을 생성한다.
DataOutputStream( OutputStream out ) OutputStream을 생성자의 매개변수로 받아
DataOutputStream을 생성한다.

또 DataInputStream은 다음과 같이 각 자료형별 메서드를 제공하여 자료형에 따라 읽거나 쓸 때 사용할 수 있다.

메서드 설명
byte readByte( ) 1바이트를 읽어 반환한다.
boolean readBoolean( ) 읽은 자료가 0이 아니면 true를, 0이면 false를 반환한다.
char readChar( ) 한 문자를 읽어 반환한다.
short readShort( ) 2바이트를 읽어 정수 값을 반환한다.
int readInt( ) 4바이트를 읽어 정수 값을 반환한다.
long readLong( ) 8바이트를 읽어 정수 값을 반환한다.
float readFloat( ) 4바이트를 읽어 실수 값을 반환한다.
double readDouble( ) 8바이트를 읽어 실수 값을 반환한다.
String readUTF( ) 수정된 UTF-8 인코딩 기반으로 문자열을 읽어 반환한다.

DataOutputStream은 각 자료형별로 read( )에 대응되는 write( ) 메서드를 제공한다.

메서드 설명
void writeByte( int v ) 1바이트의 자료를 출력한다.
void writeBoolean( Boolean v ) 1바이트 값을 출력한다.
void writeChar( char v ) 2바이트를 출력한다.
void writeShort( short v ) 2바이트를 출력한다.
void writeInt( int v ) 4바이트를 출력한다.
void writeLong( long v ) 8바이트를 출력한다.
void writeFloat( float v ) 4바이트를 출력한다.
void writeDouble( double v ) 8바이트를 출력한다.
void writeUTF( String str ) 수정된 UTF-8 인코딩 기반으로 문자열을 출력한다.

자료형을 그대로 읽고 쓰는 스트림이기 때문에 같은 정수라도 자료형에 따라 다르게 처리한다.

따라서 자료를 쓸 때 사용한 메서드와 같은 자료형의 메서드로 읽어야 한다.

또 파일이든 네트워크든 자료를 쓸 때 사용한 메서드 순서대로 읽어야 한다.

 

다음은 파일에 여러 자료형 값을 저장하는 예제이다.

자료형을 유지하여 저장하기 위해 DataInputStream과 DataOutputStream을 보조 스트림으로 사용한다.

 

예제02_DataInputStream / DataOutputStream 테스트하기

 

파일 스트림을 만들고 여기에 DataInputStream과 DataOutputStream 기능을 추가했다.

기반 스트림에서 쓸 수 없던 각 자료형의 자료를 그대로 읽고 쓸 수 있다.

파일에 쓴 것과 동일한 순서, 동일한 메서드로 읽어야 한다.