SHUSTORY

15-3 바이트 단위 스트림 본문

프로그래밍/JAVA

15-3 바이트 단위 스트림

어서오시우 2023. 3. 21. 16:39
728x90

InputStream

바이트 단위로 읽는 스트림 중 최상위 스트림이다.

InputStream은 추상 메서드를 포함한 추상 클래스로서 하위 스트림 클래스가 상속받아 각 클래스 역할에 맞게

추상 메서드 기능을 구현한다.

주로 사용하는 하위 클래스는 다음과 같다.

스트림 클래스 설명
FileInputStream 파일에서 바이트 단위로 자료를 읽는다.
ByteArrayInputStream Byte 배열 메모리에서 바이트 단위로 자료를 읽는다.
FilterInputStream 기반 스트림에서 자료를 읽을 때 추가 기능을 제공하는 보조 스트림의 상위 클래스이다.

InputStream은 바이트 자료를 읽기 위해 다음 메서드를 제공한다.

메서드 설명
int read( ) 입력 스트림으로부터 한 바이트의 자료를 읽는다.
읽은 자료의 바이트 수를 반환한다.
int read( byte b[ ] ) 입력 스트림으로부터 b[ ] 크기의 자료를 b[ ]에 읽는다.
읽은 자료의 바이트 수를 반환한다.
int read( byte b[ ], int off, int len ) 입력 스트림으로부터 b[ ] 크기의 자료를 b[ ]의 off 변수 위치부터 저장하며 len만큼 읽는다.
읽은 자료의 바이트 수를 반환한다.
void close( ) 입력 스트림과 연결된 대상 리소스를 닫는다.

read( ) 메서드의 반환형은 int로, 한 바이트를 읽어서 int에 저장한다.

한 바이트만 읽는데 반환형이 int인 이유는 더 이상 읽어 들일 자료가 없는 경우 정수 -1이 반환되기 때문이다.

파일에서 자료를 읽는 경우 파일의 끝에 도달하면 -1이 반환된다.

 

 

FileInputStream

FileInputStream은 파일에서 바이트 단위로 자료를 읽어 들일 때 사용하는 스트림 클래스이다.

스트림을 사용하기 위해서는 먼저 스트림 클래스를 생성해야 한다.

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

생성자 설명
FileInputStream( String name ) 파일 이름 name( 경로 포함 )을 매개변수로 받아 입력 스트림을 생성한다.
FileInputStream( File f ) File 클래스 정보를 매개변수로 받아 입력 스트림을 생성한다.

FileInputStream( String name ) 생성자로 스트림을 생성하여 파일로부터 자료를 읽는 코드를 살펴보자.

 

예제01_FileInputStream 사용하기

 

위 예제에서 아직 input.txt 파일이 존재하지 않기 때문에 읽으려는 파일이 없을 경우 발생하는 예외 FileNotFoundException이 발생한다. 따라서 IOException( FileNotFoundException의 상위 클래스 )이 발생한다.

그리고 finally 블록에서 열려있는 스트림을 닫기 위해 close( )를 호출하는데, 스트림이 생성되지 않았으므로

NullPointerException이 발생한다.

NullPointerException은 처리하지 않을 때 컴파일 오류가 발생하는 예외가 아니므로 어떤 예외 클래스로 처리해야 할지 잘 모를 경우 최상위 예외 클래스 Exception을 사용하면 된다.

 

 

파일에서 자료 읽기

실제 파일에서 자료를 읽어 보자.

FileInputStream("input.txt")와 같이 쓰면 제일 먼저 input.txt. 파일을 프로젝트 폴더에서 찾는다.

따라서 임의로 파일을 만들어 준다.

New File - File - CH15( 현재 프로젝트 폴더 ) - input.txt 파일 생성

 

이렇게 생성한 파일에 ABC를 적어본 후 예제01을 다시 실행해보면 다음과 같이 각 알파벳의 아스키 코드 값이 출력된다.

system.out의 read( ) 메서드는 한 바이트씩 자료를 읽어 들이기 때문이다.

이를 A, B, C로 화면에 출력하려면 System.out.println((char)fis.read( )); 와 같이 char형으로 변환하여 출력하면 된다.

 

 

파일 끝까지 읽기

예제01에서는 읽어들이고자 하는 파일에 문자 세 개가 포함된 것을 알고 있기 때문에 read( ) 메서드를 세 번 호출해서 파일에서 문자를 읽어 들였다.

하지만 파일에 내용이 얼만큼 있는지 모르는 경우 파일의 끝에 도달할 때까지 반복해서 읽어야 한다.

다음은 try-with-resources문을 사용하여 input.txt 파일을 끝까지 읽도록 구현한 예제이다.

 

예제02_파일 끝까지 읽기

 

read( ) 메서드로 파일을 읽는 경우 파일의 끝에 도달하면 -1을 반환한다.

while문을 보면 read( ) 메서드를 사용하여 한 바이트씩 읽어 들이고 있다.

읽어 들여 저장한 i값이 -1이 아닌 한 while문이 계속 수행된다.

 

 

int read( byte[  ] b ) 메서드 읽기

자료를 read( ) 메서드로 한 바이트씩 읽는 것보다 배열을 사용하여 한꺼번에 많이 읽으면 처리 속도가 훨씬 빠르다.

read( byte[ ] b ) 메서드는 선언한 바이트 배열의 크기만큼 한꺼번에 자료를 읽은 후 읽어 들인 자료의 수를 반환한다.

 

input2.txt 파일을 만들고 A~Z까지 적은 후에 바이트 배열 생성 후 배열을 사용하여 자료를 읽어 보자.

실제로 더 큰 배열을 사용하지만, 테스트를 위해 10바이트 크기 배열을 만들어 사용해보겠다.

 

예제03_byte 배열로 읽기

 

배열 크기는 10이고 26개 알파벳을 읽으므로 반복할 때마다 읽는 알파벳 개수는 10, 10, 6이다.

그런데 출력 결과 6바이트를 읽었는데 UVWXYZQRST가 출력된 것을 볼 수 있다.

그 이유는 마지막으로 남아 있는 U~Z까지 출력한 뒤 남은 4개 공간에는 기존 자료가 남아 있기 때문이다.

그렇다면 당시 읽어들인 자료만 출력하고 싶을 경우에는 향상된 for문을 사용하는 것이 아닌 다음과 같이 변경하면 된다.

 

예제03_for문 수정

 

OutputStream

바이트 단위로 쓰는 스트림 중 최상위 스트림이다. 자료의 출력 대상에 따라 다른 스트림을 제공한다.

스트림 클래스 설명
FileOutputStream 바이트 단위로 파일에 자료를 쓴다.
ByteArrayOutputStream Byte 배열에 바이트 단위로 자료를 쓴다.
FilterOutputStream 기반 스트림에서 자료를 쓸 때 추가 기능을 제공하는 보조 스트림의 상위 클래스이다.

OutputStream에서 제공하는 메서드는 다음과 같다.

메서드 설명
void write( int b ) 한 바이트를 출력한다
void write( byte[ ] b ) b[ ] 배열에 있는 자료를 출력한다.
void write( byte b[ ], int off, int len ) b[ ] 배열에 있는 자료의 off 위치부터 len 개수만큼 자료를 출력한다.
void flush( ) 출력을 위해 잠시 자료가 머무르는 출력 버퍼를 강제로 비워 자료를 출력한다.
void close( ) 출력 스트림과 연결된 대상 리소스를 닫는다.
출력 버퍼가 지워진다.

 

 

FileOutputStream

파일에 바이트 단위 자료를 출력하기 위해 사용하는 스트림이다.

FileOutputStream을 생성하는 생성자는 다음과 같다.

생성자 설명
FileOutputStream( String name ) 파일 이름 name( 경로 포함 )을 매개변수로 받아 출력 스트림을 생성한다.
FileOutputStream( String name, boolean append ) 파일 이름 name( 경로 포함 )을 매개변수로 받아 출력 스트림을 생성한다.
append 값이 true이면 파일 스트림을 닫고 다시 생성할 때 파일의 끝에 이어서 쓴다. 디폴트 값은 false이다.
FileOutputStream( File f, ) File 클래스 정보를 매개변수로 받아 출력 스트림을 생성한다.
FileOutputStream( File f, boolean append ) File 클래스 정보를 매개변수로 받아 출력 스트림을 생성한다.
append 값이 true이면 파일 스트림을 닫고 다시 생성할 때 파일의 끝에 이어서 쓴다. 디폴트 값은 false이다.

생성자 매개변수로 전달한 파일이 경로에 없으면 FileOutputStream은 파일을 새로 생성한다.

FileOutputStream을 사용해 파일에 자료를 쓸 때 기존 파일의 내용이 있더라도 처음부터 새로 쓸지( overwrite ),

아니면 기존 내용 맨 뒤에 연결해서 쓸 것인지( append ) 여부를 FileOutStream 생성자의 매개변수로 전달한다.

 

 

write( ) 메서드 사용하기

FileOutputStream을 생성하고 write( ) 메서드를 활용하여 파일에 정수 값을 저장해보자.

 

예제04_파일에 한 바이트씩 출력하기

 

8행에서 output.txt 파일 이름으로 FileOutputStream을 생성한다.

write( ) 메서드에 따라 파일에 값을 출력(쓰고)하고 스트림을 닫는다.

이클립스에서 refresh를 누르면 생성된 output.txt 파일이 보인다.

FileOutputStream은 숫자를 해당 아스키 코드 값의 문자로 변환하여 저장한다.

 

앞에서 실행한 예제04에서 fos = new FileOutputStream("output.txt", true); 로 변경한 후 실행하면

다음과 같이 이어서 출력된 것을 확인할 수 있다.

 

 

write( byte[ ] b ) 메서드 사용하기

출력도 입력과 마찬가지로 여러 자료를 한꺼번에 출력하면 효율적일 뿐더러 실행 시간도 줄어든다.

따라서 바이트 배열을 활용하여 출력할 수 있다.

write( byte[ ] b ) 메서드는 바이트 배열에 있는 자료를 한꺼번에 출력한다.

 

예제05_파일에 바이트 배열로 출력하기

 

자바 9부터 제공하는 향상된 try-with-resources문을 활용해 작성해보았다.

자바 8이하는 오류가 발생하므로 9행의 try문을 다음과 같이 변경하여 작성하도록 한다.

try(FileOutputStream fos = new FileOutputStream("output2.txt",true)){...}

 

 

write( byte[ ] b, int off, int len ) 메서드 활용하기

write( byte[ ] b, int off, int len ) 메서드는 배열의 전체 자료를 출력하지 않고 배열의 off 위치부터 len 길이만큼 출력한다.

 

예제06_파일에 바이트 배열로 출력하기

 

fos.write( bs, 2, 10)를 사용하여 두 번째 인덱스( 세 번째 위치 )부터 10개를 출력한다.

출력 파일 output3.txt를 확인하면 다음과 같다.

 

 

flush(  ) 메서드와 close( ) 메서드

출력 스트림에서 flush( ) 메서드의 기능은 강제로 자료를 출력하는 것이다.

 

write( ) 메서드로 값을 썼다고 해도 바로 파일이나 네트워크로 전송되지 않고 출력을 위한 자료가 쌓이는 출력 버퍼에 어느정도 자료가 모여야 출력된다.

따라서 자료의 양이 출력할 만큼 많지 않으면 write( ) 메서드로 출력했어도 파일에 쓰이지 않거나 전송되지 않을 수 있다.

이러한 경우에 flush( ) 메서드를 호출한다.

 

출력 스트림의 close( ) 메서드 안에서 flush( ) 메서드를 호출하여 출력 버퍼가 비워지면서남아 있는 자료가 모두 출력된다.

'프로그래밍 > JAVA' 카테고리의 다른 글

15-5 보조 스트림  (0) 2023.04.06
15-4 문자 단위 스트림  (0) 2023.04.04
15-2 표준 입출력  (0) 2023.03.21
15-1 자바 입출력과 스트림  (0) 2023.03.21
14-4 사용자 정의 예외  (0) 2023.03.17