18장 데이터 입출력

 

18.1 입출력 스트림

● 데이터 입출력

데이터는 키보드로 입력될 수 있고, 파일 또는 프로그램으로부터 입력될 수 있다. 반대로 데이터는 모니터로 출력될 수 있고, 파일에 저장되거나 다른 프로그램으로 전송될 수 있다.

 

● 스트림(Stream)

단방향으로 데이터가 흐르는 것을 의미.

○ 바이트 스트림 : 그림, 멀티미디어, 문자 등 모든 종류의 데이터를 입출력할 때 사용

○ 문자 스트림 : 문자만 입출력할 때 사용

구분 바이트 스트림 문자 스트림
최상위 클래스 InputStream OutputStream Reader Writer
하위 클래스
(예)
XXXInputStream
(FileInputStream)
XXXOutputStream
(FileOutputStream)
XXXReader
(FileReader)
XXXWriter
(FileWriter)

→ 최상위 클래스를 상속받는 하위 클래스들은 접미사로 최상위 클래스의 이름을 갖는다.

 

 

18.2 바이트 출력 스트림

OutputStream은 바이트 출력 스트림의 최상위 클래스로 추상 클래스이다.

모든 바이트 출력 스트림 클래스는 OutputStream 클래스를 상속받아 만들어진다.

리턴 타입 메소드 설명
void write(int b) 1byte를 출력
void write(byte[] b) 매개값으로 주어진 배열 b의 모든 바이트를 출력
void write(byte[] b, int off, int len) 매개값으로 주어진 배열 b[off]부터 len개의 바이트를 출력
(len은 인덱스를 말하는 것이 아님)
void flush() 출력 버퍼에 잔류하는 모든 바이트를 출력
void close() 출력 스트림을 닫고 사용 메모리 해제

 

 

● 1 바이트 출력

wirte(int b) 메소드는 매개값이 int(4byte)인데, 끝 1byte만 출력한다.

 

예시) 바로 코드로 보고 익숙해지자

public class WriteExample {
	public static void main(String[] args) {
		try {
			OutputStream os = new FileOutputStream("C:/Temp/test1.db");
			
			byte a = 10;
			byte b = 20;
			byte c = 30;
			
			os.write(a);  // 1바이트씩 출력
			os.write(b);
			os.write(c);
			
			os.flush();  // 내부 버퍼에 잔류하는 바이트를 출력하고 버퍼를 비움
			os.close();  // 출력 스트림을 닫아 사용한 메모리 해제
		} catch (IOException e) {
		}
	}
}

FileOutputStream("데이터의 도착지")해당 파일이 없으면 파일을 자동적으로 생성하고 있으면 중복 저장한다.

FileOutputStream 생성자는 주어진 파일이 없으면 IOException을 발생시킨다.

write(), flush(), close() 메소드도 마찬가지. 예외처리 해주어야 한다.

 

OutputStream은 내부에 작은 버퍼를 가지고 있다. write() 메소드가 호출되면 버퍼에 바이트를 우선 저장, 버퍼가 차면 순서대로 바이트를 출력한다.

 

 

● 바이트 배열 출력

일반적으로 1 바이트를 출력하는 경우는 드물고, 바이트 배열을 통째로 출력하는 경우가 많다.

 

사용 예시)

byte[] array = {10,20,30};
os.write(array);

 

 

18.3 바이트 입력 스트림

InputStream은 바이트 입력 스트림의 최상위 클래스로, 추상 클래스이다.

모든 바이트 입력 스트림은 InputStream 클래스를 상속받아 만들어짐.

리턴 타입 메소드 설명
int read() 1byte를 읽은 후 읽은 바이트를 리턴
int read(byte[] b) 읽은 바이트를 매개값으로 주어진 배열에 저장 후 읽은 바이트 수를 리턴
void close() 입력 스트림을 닫고 사용 메모리 해제

 

 

● 1 바이트 읽기

read() 메소드는 입력 스트림으로부터 1byte를 읽고 int(4byte) 타입으로 리턴.

(4바이트 중 끝 1바이트에만 데이터가 들어가 있음)

→ 더 이상 입력 스트림으로부터 바이트를 읽을 수 없으면 -1을 리턴한다. 이것을 이용하면 읽을 수 있는 마지막 바이트까지 반복해서 한 바이트씩 읽을 수 있다.

 

예시) 코드를 보고 익숙해지자

import java.io.*;

public class ReadExample {
	public static void main(String[] args) {
		try {
			InputStream is = new FileInputStream("C:/Temp/test1.db");
			
			while(true) {
				int data = is.read();
				if(data == -1) {
					break;
				}
				System.out.println(data);
			}
			is.close();
		} catch (IOException e) {
		}
	}
}

FileInputStream 생성자는 주어진 파일이 존재하지 않을 경우 FileNotFoundException을 발생시킨다.

read(), close() 메소드에서 IOException이 발생할 수 있으므로 두 가지 예외 모두 처리해야 한다.

 

 

● 바이트 배열로 읽기

read(byte[] b) 메소드는 입력 스트림으로부터 주어진 배열의 길이만큼 바이트를 읽고 배열에 저장한 다음 읽은 바이트 수를 리턴한다.

 

예시)

InputStream is = new FileInputStream("C:/Temp/test1.db");
    byte[] data = new byte[100];
    while(true) {
        int num = is.read(data);
        if(num == -1) break;
    }
    is.close();

 

 

18.4 문자 입출력 스트림

바이트 입출력 스트림 → InputStream, OutputStream

문자 입출력 스트림 → Reader, Writer

 

 

● 문자 출력

Writer는 문자 출력 스트림의 최상위 클래스이며 추상 클래스이다.

모든 문자 스트림 클래스는 Writer 클래스의 상속을 받음.

리턴 타입 메소드 설명
void write(int c) 매개값으로 주어진 한 문자 출력
void write(char[] cbuf) 매개값으로 주어진 배열의 모든 문자 출력
void write(char[] cbuf, int off, int len) 매개값으로 주어진 배열에서 cbuf[off]부터 len개까지 문자 출력
void write(String str) 매개값으로 주어진 문자열 출력
void write(String str, int off, int len) 매개값으로 주어진 문자열에서 off순번부터 len개까지 문자를 출력
void flush() 버퍼에 잔류하는 모든 문자 출력
void close() 출력 스트림을 닫고 사용 메모리 해제

Writer는 OutputStream과 사용 방법은 동일하지만, 출력 단위가 문자(char).

문자열을 출력하는 write(String str) 메소드를 추가로 제공.

 

예시)

Writer writer = new FileWriter("C:/temp/test.txt");

// 1 문자씩 출력
char a = 'A';
writer.write(a);
char b = 'B';
writer.write(b);

// char 배열 출력
char[] arr = {'C', 'D', 'E'};
writer.write(arr);

// 문자열 출력
writer.write("FGH");

// 버퍼에 잔류하고 있는 문자들 출력, 버퍼를 비움
writer.flush();

// 출력 스트림 닫고 메모리 해제
writer.close();

 

 

● 문자 읽기

Reader는 문자 입력 스트림의 최상위 클래스이며 추상 클래스이다.

모든 문자 입력 스트림 클래스는 Reader 클래스를 상속받아 만들어진다.

메소드 설명
int read() 1개의 문자를 읽고 리턴
int read(char[] cbuf) 읽은 문자들을 매개값으로 주어진 문자 배열에 저장하고 읽은 문자 수 리턴
void close() 입력 스트림을 닫고, 사용 메모리 해제

Reader는 InputStream과 사용 방법은 동일하지만, 출력 단위가 문자(char).

 

예시)

Reader reader = null;

reader = new FileReader("C:/temp/test.txt");
while(true) {
    int data = reader.read();
    if(data == -1) break;
    System.out.print((char)data);
}
reader.close();
System.out.println();

reader = new FileReader("C:/temp/test.txt");
char[] data = new char[100];
while(true) {
    int num = reader.read(data);
    if(num == -1) break;
    for(int i=0; i<num; i++) {
        System.out.print(data[i]);
    }
}
reader.close();

 

 

18.5 보조 스트림

다른 스트림과 연결되어 여러 가지 편리한 기능 제공.

보조 스트림은 자체적으로 입출력을 수행할 순 없다.

따라서 입출력 소스로부터 직접 생성된 입출력 스트림에 연결하여 사용.

보조스트림 변수 = new 보조스트림(입출력스트림);

보조 스트림은 또 다른 보조 스트림과 연결되어 스트림 체인으로 구성할 수 있음.

 

예시)

InputStream is = new FileInputStream(" ... ");
InputStreamReader reader = new InputStreamReader(is);
BufferedReader br = new BufferedReader(reader);

 

● 자주 사용되는 보조 스트림

보조 스트림 기능
InputStreamReader 바이트 스트림을 문자 스트림으로 변환
BufferedInputStream, BufferedOutputStream,
BufferedReader,  BufferedWriter
입출력 성능 향상
DataInputStream,  DataOutputStream 기본 타입 데이터 입출력
PrintStream,  PrintWriter 줄바꿈 처리 및 형식화된 문자열 출력

 

 

18.6 문자 변환 스트림

바이트 스트림(InputStream, OutputStream)에서 입출력할 데이터가 문자라면

문자 스트림(Reader, Writer)으로 변환해서 사용하는 것이 좋다.

문자로 바로 입출력하는 편리함과 문자셋의 종류를 지정할 수 있기 때문

 

● InputStream → Reader 변환

InputStream is = new FileInputStream("C:/Temp/test.txt");
Reader reader = new InputStreamReader(is);

 

● OutputStream → Writer 변환

OutputStream os = new FileOutputStream("C:/Temp/test.txt");
Writer writer = new OutputStreamWriter(os);

 

사용 예시)

import java.io.*;
import java.util.Scanner;

public class CharacterConvertStreamExample {
	
	public static void main(String[] args) throws IOException{
			write("문자 변환 스트림을 사용합니다.");
			String data = read();
			System.out.println(data);
	}	

	// OutputStream을 Writer로 바꿔보자
	public static void write(String str) throws IOException {
		OutputStream os = new FileOutputStream("C:/temp/test01.txt.");
		Writer writer = new OutputStreamWriter(os, "UTF-8");
		writer.write(str);
		writer.flush();
		writer.close();
	};
	
	// InputStream을 Reader로 바꿔보자
	public static String read() throws IOException{
		InputStream is = new FileInputStream("C:/temp/test01.txt.");
		Reader reader = new InputStreamReader(is, "UTF-8");
		
		char[] data = new char[100];
		int num = reader.read(data);
		reader.close();
		
		String str = new String(data, 0, num);
		return str;
	};
}

 

 

18.7 성능 향상 스트림

CPU와 메모리가 아무리 뛰어나도 하드 디스크의 입출력이 늦어지면 프로그램의 실행 성능은 하드 디스크의 처리 속도에 맞춰진다.

네트워크로 데이터를 전송할 때도 느린 네트워크 환경이라면 컴퓨터 사양이 좋아도 게임의 속도는 느릴 수밖에 없다.

완전한 해결책은 될 수 없지만, 프로그램이 입출력 소스와 직접 작업하지 않고 중간에 메모리 버퍼(buffer)와 작업함으로써 실행 성능을 향상시킬 수 있다.

출력의 경우, 버퍼는 데이터가 쌓이기를 기다렸다가 꽉 차게 되면 데이터를 한번에 하드 디스크로 보냄으로써 출력 횟수를 줄인다.

입력의 경우에도 하드 디스크로부터 직접 읽는 것 보다는 메모리 버퍼로부터 읽는 것이 빠르다.

BufferedInputStream bis = new BufferedInputStream(바이트 입력 스트림);
BufferedOutputStream bos = new BufferedOutputStream(바이트 출력 스트림);

BufferedReader br = new BufferedReader(문자 입력 스트림);
BufferedWriter bw = new BufferedWriter(문자 출력 스트림);

'JAVA' 카테고리의 다른 글

20일차 2024 - 3 - 25 (Spring 시작)  (1) 2024.03.25
19일차 2024 - 3 - 22  (1) 2024.03.24
17일차 2024 - 3 - 20  (0) 2024.03.20
16일차 2024 - 3 - 19  (0) 2024.03.19
5일차 2024 - 3 - 4  (0) 2024.03.18

+ Recent posts