javaboiii의 JAVA/JAVA 요약정리(멘토씨리즈)

JAVA -19) 파일 입출력

javaboiii 2024. 7. 30. 17:36

JAVA

1. 자바 입출력과 슽트림

프로그램은 데이터를 외부에서 입력받아 처리하고 출력하는 구조로 되어 있습니다. 프로그램으로 들어오는 모든 값을

Input이라 하고, 출력되는 값을 Output이라고 합니다. 이를 '입출력(I/O)'이라고도 부르며 자바에서는 입출력을 처리하기 위해 별도의 I/O 패키지를 제공합니다.

여기서 데이터란, 자바 프로그램에서 처리할 수 있는 모든 데이터를 의미합니다. 디스크상에 존재하는 데이터일 수도 있고, 키보드나 마우스와 같은 외부 입력장치에서 입력되는 데이터일 수도 있으며, 인터넷을 통해 전송되는 데이터일 수도 있습니다.

 

예를 들어 디스크에 있는 <read.txt> 파일을 읽어서 <write.txt> 파일을 쓴다고 가정한다면 우선 파일에 접근하기 위한 길이 필요합니다. 또한 읽어온 데이터를 쓰기 위한 길도 필요합니다. 이렇듯 바자 프로그래밍이 디스크에 접근해 데이터를 주고 받는 작없을 도와주는 통로를 '스트림(Stream)' 또는 '가상 통로'라고 합니다.

입출력 스트림의 특징

자바에서는 입출력을 위한 입력 스트림(InputStream)과 출력 스트림(OutputStream)이 있습니다. 프로그램을 기준으로 데이터가 들어오면 입력 스트림이며 데이터가 나가면 출력 스트림입니다. 입력과 출력이 동시에 진행되는 것이 아니라 각각

독립적으로 한 가지 기능만 동작하기 때문에 데이터를 교환하기 위해서는 입력 스트림과 출력 스트림이 따라 필요합니다.

 

다음은 자바 입출력 스트림의 주요 특징입니다.

  • 스트림은 입출력 장치와 자바 프로그램 간의 연결 통로로 스트림의 양 끝에서 전달이 이루어집니다.
  • 단방향 통신이므로 입력 스트림과 출력 스트림을 별도로 사용해야 하며 FIFO(First In First Out) 구조를 이루고 있습니다.
  • 연속된 데이터의 흐름으로 입출력 진행 시 다른 작업을 할 수 없는 상태가 됩니다.
  • 입출력 대상을 변경하기 편하며 동일한 프로그램 구조를 유지할 수 있습니다.

2. 입출력 스트림의 종류

자바의 기본적인 데이터 입출력은 java.io 패키지에서 제공합니다. java.io 패키지에서는 파일 시스템의 정보를 얻기 위한 File 클래스와 입출력을 위한 다양한 스트림 클래스를 제공하는데 스트림의 종류를 크게 분류하면 전달 방식에 따라 바이트(byte) 기반 스트림과 문자(char) 기반 스트림으로 구분합니다. 바이트 기반 스트림은 데이터를 컴퓨터의 기본 단위인 byte 단위로 나누어 읽거나 쓰고, 문자 기반 스트림은 텍스트 기반 문서를 다루기 위해 사용하는 스트림입니다.

 

바이트 기반 슽트림의 최상위 객체는 InputStream과 OutputStream이며, 문자 기반 스트림의 최상위 객체는 Reader와

Writer입니다. 바이트 기반 문자 기반 스트림의 특징은 다음과 같습니다.

바이트 기반 스트림 문자 기반 스트림
byte 단위로 파일을 읽고 출력할 때 사용
모든 타입의 데이터 가능
문자 단위로 파일을 읽고 출력할 때 사용
문자 데이터만 가능

 

바이트 기반 스트림은 파일의 단위인 byte 단위로 데이터를 입출력하는 스트림이므로 그림, 멀티미디어, 문자 등 모든 종류의 데이터를 주고 받을 수 있습니다. 반면 문자 기반 스트림은 문자 단위로 데이터를 입출력하는 스트림이므로 오직 텍스트로 이루어진 문서만 주고받을 수 있습니다.

3. 바이트(byte) 기반 스트림

컴퓨터의 모든 데이터는 바이트(byte) 단위로 이루어져 있습니다. 따라서 바이트 기반 스트림의 경우 모든 타입의 데이터를 읽고 쓰는 것이 가능합니다. 바이트 기반 스트림은 바이트 입력 스트림과 바이트 출력 스트림이 있습니다.

InputStream : 바이트 입력 스트림

InputStream은 모든 바이트 기반 입력 스트림이 기본적으로 가져야할 메서드들이 다음과 같이 정의되어 있습니다.

메서드 설명
int read() 문자를 1byte씩 일고 반환 / 더 이상 읽을 문자가 없으면 -1을 반환
int read(byte[] b) 매개변수로 주어진 배열에 읽은 문자를 저장하고 실제로 읽은 수만큼 반환
더 이상 읽을 문자가 없으면 -1을 반환
int read(byte[], int offset, int len) 매개변수로 주어진 배열에 정해진 범위만큼 읽어서 저장
시작 위치(offset), 길이(len)
int available() 스트림으로부터 읽어올 수 있는 데이터의 크기를 반환
close() 스트림 사용을 종료하고 자원을 반환

FileInputStream

FileInputStream은 파일에서 바이트 단위로 자료를 읽어 들일 때 사용하는 스트림입니다. 파일을 읽기 위한 FileInputStream을 선언하는 방법은 다음과 같습니다.

new FileInputStream(경로/파일명);

매개변수로 파일명을 포함한 경로를 지정합니다. 이미지, 동영상, 텍스트 등 모든 타입의 파일을 읽어올 수 있습니다.

read()메서드

FileInputStream을 사용하면 파일을 읽어올 수 있습니다. 디스크에 저장된 문서 또는 인터넷상에서 받을 수 있는 문서들을 읽어서 출력할 수 있습니다.

read() 메서드는 데이터를 1byte씩 읽어서 int 타입으로 리턴합니다. 더 이상 읽을 내용일 없다면 -1을 반환하여 데이터를 다 읽은 시점을 알 수 있습니다. 

 

read() 메서드는 데이터를 바이트 타입으로 읽어오기 때문에 문자를 출력할 때는 형 변환하여 출력해야 합니다.

프로그램 종료 전에 입출력 I/O의 경우 close() 해주는 것이 좋습니다. finally는 예외 발생 유무와 상관없이 실행됨으로

해당 위치에서 닫아주는 것이 좋습니다. 모든 입출력 기능은 CheckedException을 따르기 때문에 예외 처리를 해주어야 합니다.

read(byte[] b) 메서드

read() 메서드는 데이터를 1byte씩 읽기 때문에 속도가 느리다는 단점이 있습니다. 반면에 read(byte[]) 메서드는 데이터를

배열에 담아서 읽으므로 데이터를 읽는 횟수가 현저히 줄어듭니다. 따라서 데이터를 읽는 속도를 향상시킬 수 있습니다.

 

배열을 이용하여 한 번에 여러 개의 데이터를 가져오면 횟수가 감소되므로 파일 읽는 속도가 향상 되지만

주의할 점이 있습니다.

System.out.print(new String(buffer, 0, length));

해당 문법은 배열에 있는 데이터를 출력할 때 사용합니다. length는 read() 메서드가 읽은 만큼의 수를 반환한 값을 사용하는데 만약 다음과 같이 배열의 전체 길이 값을 사용하면 문자가 이어서 출력됩니다.

 

배열은 선언한 후에는 크기가 늘거나 줄어들지 않습니다. 배열의 크기보다 남아있는 데이터가 작거나 이전에 데이터를 저장한 경우에는 새로 입력된 데이터 이후 기존에 입력했던 잔재가 남게 됩니다. 따라서 읽은 만큼만 가져오지 않는다면 이전에 저장했던 데이터가 출력됩니다. 배열을 이용한 읽기 방식에서는 이 부분을 유의해서 진행해야 합니다.

 

바이트 기반 입력 스트림으로 파일을 읽을 때 rea() 메서드를 통해 읽으면 한글의 경우 깨짐 현상이 일어납니다. 그 이유는 글자를 바이트 타입으로 변경하고 읽은 후, 다시 문자형으로 변환할 때 정상적으로 변환되지 않기 때문입니다.

만약 바이트 기반으로 한글을 읽는다면 read(byte[] buffer) 메서드를 사용해야 합니다. 배열을 이용해 글자를 읽어오기 때문에 단어 그대로 저장해서 가져올 수 있습니다.

OutputStream : 바이트 출력 스트림

바이트 기반의 출력 스트림은 최상위 클래스로 OutputStream 객체가 제공됩니다. 해당 객체를 상속해 다양한 출력 스트림들이 존재합니다.

각자의 개발환경에 맞게 선택하여 사용하면 됩니다. 

OutputStream은 모든 바이트 기반 출력 스트림이 기본적으로 가져야할 메서드들이 다음과 같이 정의되어 있습니다.

메서드 설명
int write(int b) 1byte 출력
int write(byte[] b) 매개변수로 주어진 배열의 모든 바이트 출력
int write (byte[] b, int offset, int len) 매개변수로 주어진 배열에 정해진 범위만큼 읽어서 출력
시작 위치(offset), 길이(len)
void flush() 출력 버퍼에 잔류하는 모든 내용 출력
close() 스트림 사용을 종료하고 자원 반환

FileOutputStream

파일을 쓰기 위한 FileOutputStream을 선언하는 방법은 다음과 같습니다.

new FileOutputStream(경로/파일명, 이어쓰기 옵션);

파일을 쓸 때 기존 파일명이 존재하는 경우가 있습니다. 이때 해당 파일의 내용을 유지한 채 이어 쓰거나 기존의 내용을 무시하고 새롭게 파일을 생성할 수 있습니다. 어이 쓰기 옵션이 true이면 기존 파일에 이어서 내용을 추가하고, false이면 기존 내용을 무시하고 새로 쓰게됩니다. 기본 옵션은 false로 되어 있습니다.

write(int b) 메서드

write(int b) 메서드는 1byte 단위로 데이터를 받아 출력하는 메서드입니다. 단일 데이터를 매겨변수로 받아서 출력 대상에 쓰게 됩니다.

write(byte[] b) 메서드

write(byte[] b)메서드는 파일을 쓸 때, 배열을 통해서 여러 데이터를 한 번에 출력할 수 있습니다. 많은 양의 데이터를 파일에 저장할 때 유용합니다.

4. 문자 기반 스트림

자바에서 기본 자료형인 char형을 통해 문자를 저장할 수 있습니다. 1byte 단위로 처리하는 바이트 기반 스트림은 모든 파일을 다룰 수 있으나 문자를 처리하는 char형의 크기는 2byte로 별도의 처리를 하지 않으면 정상적으로 읽지 못하는 경우가 있습니다. 이때 문자 기반 스트림을 사용하면 간단하게 문자를 처리할 수 있습니다. 문자 기반 스트림은 문자 입력 스트림과 문자 출력 스트림이 있습니다.

Reader : 문자 입력 스트림

문자 기반의 입력 스트림은 최상위클래스인 Reader를 상속해 다양한 클래스를 제공합니다.

Reader는 모든 문자 기반 입력 스트림이 기본적으로 가져야할 메서드들이 다음과 같이 정의되어 있습니다.

메서드 설명
int read() 1개의 문자를 읽고 반환
더 이상 읽을 문자가 없으면 -1을 반환
 int read(char[] cbuf) 매개변수로 주어진 배열에 읽은 문자를 저장하고 읽은 수만큼 반환
더 이상 읽을 문자가 없으면 -1을 반환
int read(char[] cbuf, int offset, int len) 매개변수로 주어진 배열에 정해진 범위만큼 읽어서 저장
시작위치(offset), 길이(len)
close() 스트림 사용을 종료하고 자원을 반환

Reader의 주요 메서드들을 통해 문자 데이터를 읽어올 수 있습니다.

FileReader

FileReader는 앞에서 학습한 FileInputStream의 기능들과 메서드 이름이 같습니다. 기능의 사용법도 크게 다르지 않습니다.

read() 메서드

FileReader의 read() 메서드는 문자 단위로 읽어서 int형으로 반환합니다. 따라서 한글이 깨지지 않고 출력됩니다.

read(char[] cbuf) 메서드

FileReader 스트림의 read(char[] cubf) 메서드는 FileInputStream의 내용과 동일합니다. 다른 점은 byte 배열이 아닌

char 배열을 매개 변수로 사용한다는 점입니다. 한 글자씩 읽는 read() 메서드에 비해 배열 단위로 문자를 읽기 때문에

익는 횟수가 현저히 줄어들어 속도에서 효율성을 높입니다.

Writer : 문자 출력 스트림

문자 기반의 출력 스트림은 최상위 클래스 Writer를 상속해 당양한 클래스를 제공합니다.

모든 Writer 클래스를 상속해 다양한 기능을 구현하고 있습니다. Writer는 모든 문자 기반 출력 스트림이 기본적으로 가져야 할 메서드들이 다음과 같이 정의되어 있습니다.

메서드 설명
int write(char ) 단일 문자 출력
int write(char[]  ) 매개변수로 주어진 배열의 모든 문자 출력
int write(String ) 문자열을 매개변수로 주어진 문자열 출력
int write(char[] , int offset, int len) 매개변수로 주어진 배열에 정해진 범위만큼 읽어서 출력
시작 위치(offset), 길이(len)
void flush() 출력 버처에 잔류하는 모든 내용 출력
close() 스트림 사용을 종료하고 자원 반환

FileWriter 선언

파일을 출력하기 위한 FileWriter를 선언하는 방법은 다음과 같습니다.

new FileWriter(경로/파일명, 이어쓰기 옵션);

FileWriter도 FileOutputStream과 마찬가지로 파일을 생성할 때, 파일의 내용을 유지한 채 이어 쓰거나 기존 내용을 무시하고 새롭게 파일을 생성할 수 있습니다.

이어쓰기 옵션이 true이면 기존 파일에 이어서 내용을 추가하고, false이면 기존 내용을 무시하고 새로 쓰게 됩니다.

기본 옵션은 false로 되어 있습니다.

write(char cbuf)메서드

write(char cbuf)는 단일 문자를 입력 받아 출력하는 메서드입니다. 한 문장씩 읽어 들여서 지정된 파일에 내용을 입력하게 됩니다.

5. 보조 스트림

스트림 기능에 따라 다음과 같이 기반 스트림과 보조 스트림으로 구분됩니다.

  • 기반 스트림 : 대상에 직접 자료를 읽고 쓰는 스트림입니다.
  • 보조 스트림 : 직접 읽고 쓰는 기능 없이 기반 스트림에 추가로 사용할 수 있는 스트림입니다.

보조 스트림은 실제로 데이터를 주고받을 수 없지만, 스트림의 기능을 향상시키거나 새로운 기능을 제공해 주는 스트림으로 다른 보조 스트림과 중첩하여 사용할 수 있습니다. 

프로그램은 입력 스트립을 통해 직접 데이터를 읽지 않고 보조 스트림과 중첩 스트림을 통해 전달받습니다. 또한 출력 슽트림으로 직접데이터를 보내지 않고 보조 스트림을 통해 전달받아 데이터를 출력합니다.

보조 스트림 연결하기

보조 스트림을 사용하려면 보조 스트림을 매개변수로 받는 기반 스트림이 먼저 선언되어야 합니다. 보조 스트림은 스스로 데이터를 읽거나 쓸 수 없기 때문에 입출력과 바로 연결되는 기반 스트림이 필요합니다.

선언하는 방법은 다음과 같습니다.

보조 스트림 변수명 = new 보조 스트림(기반 스트림);

성능 향상 보조 스트림

느린 하드디스크와 네트워크는 입출력 성능에 영향을 줍니다. 이때 입출력 소스와 직접 작업하지 않고 버퍼라는 메모리를

이용해 작업하면 실행 성능을 향상시킬 수 있습니다. 하지만 버퍼는 크기가 작아 많은 양의 데이트를 처리하기에는 부족합니다. 그래서 보조 스트림 중에서는 다음과 같이 메모리 버퍼를 추가로 제공하여 스트림의 성능을 향상시키는 것들이 있습니다.

  • 바이트 기반 스트림 : BufferedInputStream과 BufferedOutputStream
  • 문자 기반 스트림 : BufferdReader와 BufferedWriter

BufferedInputStream과 BufferedOutputStream

BufferedInputStream은 바이트 기반 스트림인 InputStream에 연결되어 버퍼를 제공해 주는 보조 스트림이고 

BufferedOutputStream 은 OutputStream에 연결되어 버퍼를 제공해 주는 보조 스트림입니다.

프로그램은 입력 장치로부터 데이터를 직접 읽는 대신 버퍼에 저장해 두었다가 큰 단위로 받기 때문에 속도가 향상됩니다.

또한 출력에 대한 보조 스트림은 프로그램에서 전송한 데이터를 내부 버퍼에 쌓아두었다가 버퍼가 꽉 차면, 모든 데이터를 한꺼번에 전송합니다.

프로그램의 입장에서는 직접 데이터를 보내는 것이 아니라, 버퍼를 통해 한꺼번에 전송함으로써 입출력 횟수를 줄여 실행 성능이 향상되는 효과를 볼 수 있습니다.

보조 스트림의 사용

보조 스트림의 객체 선언은 기반 스트림을 생성자의 매개변수로 하여 선언합니다.

다음과 같이 목적에 맞는 기반 스트림을 선언한 후, 보조 스트림을 선언할 때 매개변수로 전달하여 해당 기반 스트림을 보조합니다.

InputStream in = new ....
BufferedInputStream bis = new BufferedInputStream(in);

 

보조 스트림의 메서드는 기반 스트림의 메서드와 형태 및 기능이 비슷하게 때문에 사용상의 어려움은 없습니다.

 

보조 스트림을 사용하면 성능이 훨씬 좋아집니다.

 

FileInputStream / FileOutputStream은 단방향으로 진행됩니다. 따라서 한 번 파일을 읽으면 객체를 다시 쓰기 어려우므로 다시 사용할 때는 객체 사용을 종료한 후 재정의하여 사용하는 것이 좋습니다.

BufferedReader와 BufferedWriter

BufferedReader는 문자 기반 스트림인 Reader에 BufferedWriter는 Writer에 연결되어 버퍼를 제공해 주는 보조 스트림입니다. 바이트 기반 스트림과 마찬가지고 보조 스트림을 사용해 성능을 향상시킬 수 있습니다.

BufferedReader 또는 BufferedWriter의 경우, 버퍼에 데이터를 저장하여 입력 또는 출력하기 때문에 한 단어뿐만 아니라

문장 단위로 데이터를 읽거나 쓸 수 있습니다.

 

BufferdReader 보조 스트림은 문자 기반 스트림의 보조 스트림이기 때문에 문자를 버퍼에 저장하여 읽어옵니다. 따라서 텍스트 문서를 읽어 올 때 파일의 문자를 라인 단위로 저장하여 읽어올 수 있습니다.

 

BufferedWriter 스트림 역시 읽어 들인 데이터를 버퍼에 저장하여 한 번 출력하기 때문에 문장 단위로 데이터를 출력할 수 있습니다. 이렇게 보조 스트림을 적절히 잘 사용하면 데이터를 읽고 쓰는 속도를 향상시킬 수 있습니다.

문자 변환 보조 스트림

바이트 기반 스트림으로 텍스트 파일을 읽거나 쓸 경우, 한글을 포함한 비영어권 문자들이 정상적으로 출력되지 않습니다. 소스 스트림이 바이트 기반 스트림이고 입출력 데이터가 문자라면 Reader와 Writer로 변환하여 사용하는 것을 고려해야 합니다. 그 이유는 Reader와 Writer는 문자 단위로 입출력하기 때문에 바이트 기반 스트림보다 다양한 문자를 입출력할 수 있기 때문입니다.

문자 변환 보조 스트림에는 InputStreamReader와 OutputStreamWriter가 있습니다.

InputStreamReader

InputStreamReader는 바이트 기반 스트림 InputStream을 문자 기반 스트림 Reader로 변환하는 보조 스트림입니다.

 

InputStreamReader를 선언하는 방법은 다음과 같습니다.

FileInputStream in = new...;
InputStreamReader is = new InputStreamReader(in);
InputStreamReader is = new InputStreamReader(in, text-encoding);

 

InputStreamReader를 선언할 때는 text-encoding을 선택해 선언할 수 있습니다. 개발 환경의 text-encoding이 기본저긍로 지정되어 사용됩니다. 해당 인코딩은 file이 생성될 때 사용한 것과 동일하게 지정되어야 하므로 만약 개발 환경이 읽어 들이는 파일의 text-encoding과 다르다면 직접 지정해야 합니다.

OutputStreamWriter

OutputStreamWriter는 바이트 기반 스트림 OuputStream을 문자 기반 스트림 Writer로 변환하는 보조 스트림입니다.

 

OutputStreamWriter를 선언하는 방법은 다음과 같습니다.

FileOutputStream out = new...;
OutputStreamWriter is = new OutputStreamWriter(out);
OutputStreamWriter is = new OutputStreamWriter(in, text-encodig);

 

OutputStreamWriter를 선언할 때도 text-edcoing을 선택해 선언할 수 있습니다. 생성되는 파일의 text-encoding을 결정합니다.

다만 text-encoding과 다른 것을 지정하여 파일을 생성하면 해당 파일을 열 때 인코딩이 맞지 않아서 깨질 수 있으니 유의해야 합니다.

6. File 클래스

자바 패키지에서 제공하는 File 클래스는 파일 및 폴더에 대한 경로명, 파일 크기, 타입, 날짜 등의 속성 정보를 제공하고 파일 생성, 삭제, 이름 변경 등 파일 관리 작업을 지원하기 위한 메서드로 구성되어 있습니다.

파일 객체 선언

파일 객체를 선언할 때 생성자의 매개변수로 파일 경로를 받습니다.

File file = new File(경로);

 

파일 경로를 작성할 때 사용하는 구분자는 운영체제에 따라 조금씩 다릅니다. 윈도우 환경의 경우 \(역슬래시) 또는 /(슬래스) 모두 가능하지만 \를 사용할 경우 2개를 작성해야 합니다. 리눅스 환경의 경우 /를 구분자로 사용합니다.

File 객체를 생성했다고 해서 실제 폴더나 파일이 생성되는 것은 아닙니다. 또한 지정한 경로에 파일이 없어도 생성 시 오류가 발생하지 않습니다. 그 이유는 File 객체는 지정한 경로의 파일 또는 폴더를 객체화 하거나 저정된 파일이나 폴더를 새로 생성할 때 모두 사용되기 때문입니다.

파일 객체 기능

File 클래스에서 사용하는 대표적인 메서드는 다음과 같습니다.

메서드 설명
boolean delete() 파일 또는 폴더 삭제(성공 시 true 반환)
boolean createNewFile() 새로운 파일이나 내용이 없는 파일 생성
boolean mkdir() 경로의 계층이 여러 개 있어도 최상위 경로의 폴더만 생성
boolean mkdirs() 경로 상의 필요한 모든 폴더 생성
boolean exists() 폴더 또는 파일의 존재 여부 반환
boolean isFile() 파일 여부 반환
booleadn isDirectory() 폴더 여부 반환
long length() 파일 크기 반환
boolean canWrite() 쓰기 가능한 파일 여부 반환
boolean canRead() 일기 가능한 파일 여부 반환
String gerPath() 상대 경로 반환
String getAbsoluteParh() 절대 경로 반환

7. 직렬화

직렬화란 자바 시스템 내부에서 사용하는 객체나 데이터를 외부의 시스템에서 사용할 수 있도록 바이트(byte) 단위의 데이터로 변환시키는 기술을 말합니다. 즉, 클래스 형태의 데이터를 바이트 단위로 변환합니다. 바이트 단위로 변환된 데이터를 다시 객체 형태로 변환하는 것을 역직렬화라고 합니다.

객체의 직렬화

직렬화 기능을 사용하기 위해서는 머전 사용할 객체를 준비해야 합니다. 사용할 객체에 java.io.Serializable 인터페이스를 상속하여 직렬화가 가능한 객체로 만들 수 있습니다.

public class Person implements Serializable{
	private static final long serialVerionUID = 1L;
}

serialVersionUID

객체의 직렬화 코드를 살펴보면 serialVersionUID가 있습니다. 해당 값은 객체를 직렬화 또는 역직렬화할 때 서로 값이

맞는지 확인하는 기준이 되는 값입니다. 만약 직렬화하는 과정에서 해당 값이 변경된다면 InvalidClassException 예외가

발생합니다. 물론 해당 값은 필수 값이 아니므로 지정하지 않는다면 클래스의 해시 값을 사용하게 됩니다.

 

해당 값을 지정할 때는 개발자가 직접 지정하는 것을 자바에서 추천하고 있는데, 이클립스의 기능을 이용해 쉽게 추가할 수 있습니다.

ObjectInputStream과 ObjectOutputStream

Serializable을 상속한 객체를 직렬화하여 전달하기 위해 객체 보조 시트림인 ObjectInputStream과 ObjectOutputStream을 사용합니다.

  • ObjectOutputStream  : 직렬화, 객체를 저장하기 위해 사용합니다. 
  • ObjectInputStream  : 역직렬화, 객체를 읽기 위해 사용합니다.

객체를 직렬화하여 저장할 경우, text 형태가 아닌 객체 자체를 데이터화하므로 파일을 열어도 내용을 볼 수 없습니다. 자바프로그래밍을 통해 ObjectInputStream을 이용해 직렬화된 데이터를 역직렬화하여 읽을 수 있습니다.

응용문제

1. 다음 중 스트림에 관한 설명으로 틀린 것은 무엇일까요 ?

① 데이터를 읽어들이는 것은 입력 스트림이라고 합니다.

② 데이터를 저장하는 것을 출력 스트림이라고 합니다.

③ 하나의 스트림으로 입력과 출력을 모두 제어할 수 있습니다.

④ 스트림은 바이트 기반 스트림과 문자 기반 스트림으로 나눌 수 있습니다.

 

정답 : ③

2. 이미지 파일을 복사하려고 합니다. 다음 중 가장 적합한 스트림은 무엇일까요 ?

① FileInputStreamm FileWriter

② FileReader, FileWriter

③ FileInputStream, FileOutputStream

④ FileReader, FileOutputStream

 

정답 : ③

3. 다음은 보조 스트림에 대한 설명입니다. 문장이 맞으면 O표, 틀리면 X표 하세요

  • 보조 스트림은 다른 보조 스트림과 중첩하여 사용할 수 있습니다. (  )
  • 보조 스트림은 자체적으로 입출력을 수행할 수 있습니다. (  )
  • 성능 향상 기반 보조 스트림 중에서 바이트 기반 스트림에는 BufferedInputStream과 BufferedOutputStream이 있으며 문자 기반 스트림에는 BufferedReader와 BufferedWriter가 있습니다. (  )
  • 문자 기반 스트림으로 최상위 스트림은 Reader 입니다. (  )

4. 다음 코드의 빈 칸을 완성해 보세요.

package section19;

public class FileReaderExample{
	public static void main(String[] args){
    	FileInputStream in = null;
        try{
        	in = new FrileInputStream("read.txt");
            int read = 0;
            
            while(빈칸){
            	read = in.read();
                System.out.println((char) read);
            }
        } catch(Exception e){
        	e.printStackTrace();
        } finally {
        	if(in != null){
            	빈칸 // 스트림 닫기
            }
        } catch(Exception e){
        	e.printStackTrace();
        }
    }
}

정답 : 

package section19;

import java.io.FileInputStream;
import java.io.IOException;

public class FileReaderExample {
    public static void main(String[] args) {
        FileInputStream in = null;
        try {
            in = new FileInputStream("read.txt");
            int read = 0;

            while ((read = in.read()) != -1) {
                System.out.println((char) read);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

5. 사용자가 키보드를 통해 입력한 내용을 파일로 출력하는 예제를 작성해 보세요. 사용자가 "END"라고 입력하면 프로그램을 종료합니다.

package section19;

public class FileWriteExam{
	public static void main(String[] args){
    	Scanner sc = new Scanner(System.in);
        FileWriter writer = null;
        
        try{
        	String str = "";
			빈칸
            System.out.println("키보드 입력 : ");
            while(true){
            	str=sc.next();
                if(str.equals("END")){
                	break;
                }
                writer.write(str);
            }
        } catch(Exception e){
        	e.printStackTrace();
        } finally {
        	try{
            	if(writer != null){
                	writer.close()
                }
                
                if(sc != null){
                	sc.close();
                }
            } catch(Exception e){
            	e.printStackTrace();
            }
        }
    }
}

정답 : 

package section19;

import java.io.FileWriter;
import java.io.IOException;
import java.util.Scanner;

public class FileWriteExam {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        FileWriter writer = null;

        try {
        	String str = "";
            writer = new FileWriter("output.txt"); // 빈칸 채우기
            System.out.println("키보드 입력 : ");
            while (true) {
                str = sc.next(); 
                if (str.equals("END")) {
                    break;
                }
                writer.write(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (writer != null) {
                    writer.close();
                }

                if (sc != null) {
                    sc.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

6. 과일의 목록이 들어있는 텍스트 파일이 있습니다. 해당 목록에는 같은 과일이 중복되어 있습니다. 해당 파일을 읽어서 각 과일이 몇 번씩 기록되어 있는지를 출력하는 프로그램을 작성해 보세요. 파일 내용은 임의로 만듭니다.

출력 결과 : 

메론 : 1번

배 : 1번

사과 : 3번

수박 : 2번

 

정답 : 

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class FruitCount {
    public static void main(String[] args) {
        Map<String, Integer> fruitCount = new HashMap<>();
        
        try (BufferedReader reader = new BufferedReader(new FileReader("fruits.txt"))) {
            String line;
            while ((line = reader.readLine()) != null) {
                fruitCount.put(line, fruitCount.getOrDefault(line, 0) + 1);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        for (Map.Entry<String, Integer> entry : fruitCount.entrySet()) {
            System.out.println(entry.getKey() + " : " + entry.getValue() + "번");
        }
    }
}

 

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;

public class FruitCount {
    public static void main(String[] args) {
        ArrayList<String> fruits = new ArrayList<>();

        try (BufferedReader reader = new BufferedReader(new FileReader("fruits.txt"))) {
            String line;
            while ((line = reader.readLine()) != null) {
                fruits.add(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        ArrayList<String> countedFruits = new ArrayList<>();
        for (int i = 0; i < fruits.size(); i++) {
            String fruit = fruits.get(i);
            if (!countedFruits.contains(fruit)) {
                int count = 0;
                for (int j = 0; j < fruits.size(); j++) {
                    if (fruit.equals(fruits.get(j))) {
                        count++;
                    }
                }
                countedFruits.add(fruit);
                System.out.println(fruit + " : " + count + "번");
            }
        }
    }
}

7. 다음 빈칸에 알맞은 단어를 작성해 보세요.

  • 바이트로 읽어 들인 데이터를 문자로 변환해 주는 스트림을 [    ]이라고 합니다.
  • 자바 시스템 내부에서 사용하는 객체나 데이터를 외부에서 사용할 수 있도록 바이틀 단위의 데이터로 변환하는 기술을 [    ](이)라고 하고 바이트로 변환된 데이터를 다시 객체로 변환하는 기술을 [   ](이)라고 합니다.

정답 : InputStreamReader, 직열화, 역직렬화