Back-end/이것이 자바다[신용권 한빛미디어]

IO 패키지 소개 및 입력, 출력 스트림 [IO 기반 입출력 및 네트워킹]

Ho's log 2022. 10. 3. 22:46

프로그램에서는 데이터를 외부에서 읽고 다시 외부로 출력하는 작업이 빈번히 일어난다.
데이터는 사용자로부터 키보드를 통해 입력 될 수도 있고, 파일 또는 네트워크로부터 입력 될 수도 있다,
데이터는 반대로 모니터로 출력 될 수도 있고, 파일로 출력되어 저장 될 수도 있으며 네트워크로 출력되어 전성 될 수도 있다.

자바에서 데이터는 스트림(Stream)을 통해 입출력되므로 스트림의 특징을 잘 이해해야 한다.
스트림은 단일 방향으로 연속적으로 흘러가는것을 말하는데. 물이 높은 곳에서 낮은 곳으로 흐르듯이 데이터는 출발지에서 나와 도착지로 들어 간다는 개념이다 

입력 스트림과 출력 스트림


프로그램이 출발지냐 또는 도착지냐에 따라서 스트림의 종류가 결정되는데, 프로그램이 데이터를 입력 받을 때에는 입력 스트림(Input Stream) 이라 부르고, 프로그램이 데이터를 보낼때에는 출력스트림(OutputStream) 이라고 부른다,

입력 스트림의 출발지는 키보드, 파일, 네트워크 상의 프로그램이 될수 있고, 출력 스트림의 도착지는 모니터, 파일, 네트워크상의 프로그램이 될수 있다.

항상 프로그램을 기준으로 데이터가 들어오면 입력 스트림이고, 데이터가 나가면 출력 스트림이라는 것을 명심해야 한다.
프로그램이 네트워크상의 다른 프로그램과 데이터 교환을 하기 위해서는 양쪽 모두 입력 스트림과 출력 스트림이 따로 필요하다.

스트림의 특성이 단방향 이므로 하나의 스트림으로 입력과 출력을 모두 할수 없기 때문이다. 

 

자바의 기본적인 데이터 입출력(IO : Input / Output) API 는 java.io 패키지에서 제공하고 있다.
java.io 패키지에는 파일 시스템의 정보를 얻기 위한 File 클래스와 데이터를 입출력하기 위한 다양한 입출력 스트림 클래스를 제공하고 있다. 

스트림 클래스는 크게 두종류로 구분 된다. 하나는 바이트(byte) 기반 스트림이고, 다른 하나는 문자(charter) 기반 스트림이다

바이트 기반 스트림은 그림, 멀티미디어, 문자 등 모든 종류의 데이터를 받고 보낼 수 있으나, 문자 기반 스트림은 오로지 문자만 받고 보낼수 있도록 특화되어 있다.

바이트 기반 스트림과 문자 기반 스트림은 최상위 클래스에 따라서 다음과 같이 구분 된다 

InputStream은 바이트 기반 입력 스트림의 최상위 클래스 이고, OutputStream은 바이트 기반 출력 스트림의 최상위 클래스이다. 이클래스들을 각각 상속받는 하위 클래스는 접미사로 InputStream 또는 OutputStream이 붙는다. 
Reader는 문자 기반 입력 스트림의 최상위 클래스이고, Writer는 문자 기반 출력 스트림의 최상위 클래스이다. 
이클래스들을 각각 상속받는 하위 클래스는 접미사로 Reader 또는 Writer 가 붙는다. 

예를 들어 그림, 멀티미디어, 텍스트 등의 파일을 바이트 단위로 읽어 들일 때에는 FileInputStream을 사용하고 , 바이트 단위로 저장할 때엔 FileOutputStream을 사용한다. 텍스트 파일의 경우, 문자 단위로 읽어들일 때에는 FileReader를 사용하고, 문자 단위로 저장할때는 FileWriter을 사용한다 

InputStream

InputStream은 바이트 기반 입력 스트림의 최상위 클래스로 추상 클래스이다. 모든 바이트 기반 입력 스트림은 이 클래스에 상속받아서 만들어진다. 

다음과 같이 FileInputStream, BufferedInputStream, DataInputStream 클래스는 모두 InputStream 클래스를 상속하고 있다.

InputStream 클래스에는 바이트 기반 입력 스트림이 기본적으로 가져야 할 메소드가 정의되어 있다.

다음은 InputStream 클래스의 주요 메서드이다

read() 메소드

read() 메소드는 입력 스트림으로부터 1바이트를 읽고 4바이트 int 타입으로 리턴한다.
따라서 리턴된 4바이트 중 끝의 1바이트에만 데이터가 들어 있다, 예를 들어 입력 스트림에서 5개의 바이트가 들어 온다면 다음과 같이 read()메소드로 1바이트씩 5번 읽을 수 있다. 

더 이상 입력 스트림으로부터 바이트를 읽을수 없다면 read() 메소드는 -1을 리턴하는데 이것을 이용하면 읽을 수 있는 마지막 바이트 까지 루프를 돌며 한 바이트씩 읽을 수 있다.

InputStream is = new FileInputStream("C:/test.jpg");
int readByte;
while((readByte=is.read()) != -1) {....};

read(byte[] b) 메소드

read(byte[] b) 메소드는 입력 스트림으로부터 매개값으로 주어진 바이트 배열의 길이만큼 바이트를 읽고 배열에 저장한다,
그리고 읽은 바이트 수를 리턴한다.
실제로 읽은 바이트 수가 배열의 길이보다 작을 경우 읽은 수만큼만 리턴한다.
예를 들어 입력 스트림에서 5개의 바이트가 들어온다면 다음과 같이 길이 3인 바이트 배열로 두번 읽을수 있다.

 

read(bytep[ b) 역시 입력 스트림으로부터 바이트를 더이상 읽을 수 없다면 -1을 리턴하는데, 이것을 이용하면 읽을 수 있는 마지막 바이트까지 루프를 돌며 읽을 수 있다.

InputStream is = new FileInputStream("C:/test.jpg");
int readByteNo;
byte[] readBytes = new byte[100];
while((readByteNo = is.read(readBytes)) != -1) {...}

입력 스트림으로부터 100개의 바이트가 들어온다면 read() 메소드는 100번을 루핑해서 읽어들어야한다. 그러나 read(byte[] b) 메소드는 한번 읽을 때 매개값으로 주어진 바이트 배열 길이만큼 읽기 때문에 루핑 횟수가 현저히 줄어든다. 그러므로 많은 양이 바이트를 읽을 때는 read(byte[] b) 메소드를 사용하는 것이 좋다 

read(byte[] b , int off, int len) 메소드

read(byte[] b, int off, int len) 메소드는 입력 스트림으로부터 len개의 바이트만큼 읽고, 매개값으로 주어진 바이트 배열 b[off]부터 len 개까지 저장한다. 그리고 읽은 바이트 수인 len 개를 리턴한다,

실제로 읽은 바이트 수가 len개보다 작을 경우 읽은 수만큼 리턴한다. 예를 들어 입력 스트림에서 전체 5개의 바이트가 들어고, 여기서 3개만 읽어 b[2], b[3], b[4]에 각각 저장한다면 다음과 같이 할 수 있다. 

 

read(byte[] b , int off, int len ) 역시 입력 스트림으로부터 바이트를 더이상 읽을수 없다면 -1을 리턶나다
read(byte[] b) 와의 차이점은 한번에 읽어들이는 바이트 수를 len매개값으로 조절 할 수 있고, 배열에서 저장이 시작되는 인덱스를 지정할 수 있다는 점이다, 
만약 off를 0으로, len을 배열의 길이로 준다면 read(byte[] b ) 와 동일하다 

InputStream is = ...;
byte [ ] readBytes = new byte[100];
int  readByteNo = is.read(readBytes)

InputStream is = ...;
byte [ ] readBytes = new byte[100];
int readByteNo = is.read(readBytes0,100)

 

close 메소드 

InputStream을 더 이상 사용하지 않을 경우에는 close() 메소드를 호출해서 InputStream에서 사용했던 시스템 자원을 풀어준다. 

is.close();