프로그래밍언어 자바 Part-2

7. 입출력 프로그래밍


이번 강좌에서는 입출력 프로그래밍의 주요 개념과 java.io 및 java.nio 패키지의 주요 클래스들을 사용한 기본적인 입출력 프로그램 개발방법을 배웁니다.

이 강의를 통해 입출력 프로그램이 무엇인지 이해하고 자바 에서 기본적인 입출력 프로그램을 구현할 수 있게 됩니다.



01: 자바 I/O 개요

1) 입출력 구현 사례

입출력은 컴퓨터 프로그램에 꼭 필요한 요소 입니다. 일반적으로 모든 프로그램은 어떠한 형태로든 데이터를 입력받고 처리하고 출력하는 구조로 되어 있습니다.

게임 프로그램
조이스틱이나 터치스크린을 통해 입력을 받고 프로그램에서 주인공을 움직이거나 아이템을 사용하도록 처리하고 결과를 그래픽 화면으로 보여주는 형태 입니다.
편의점의 계산대(POS) 프로그램
바코드리더기로 부터 상품의 바코드를 입력 받아 해당 상품을 조회하고 상품 정보를 화면에 보여줍니다.

입력과 출력은 컴퓨터와 연결된 장치(Device)를 통해 이루어지며 대표적인 입출력 장치는 다음과 같습니다.

입력장치

출력장치

이러한 입출력 장치들은 컴퓨터와는 유선 혹은 무선으로 연결될 수 있습니다. 대표적인 유선 인터페이스는 USB 이며 전통적인 장치들은 시리얼 포트를 사용합니다. 무선의 경우 블루투스가 대표적이며 이들 장치를 컴퓨터에서 인식하고 사용가능하게 하는 것은 운영체제와 장치드라이버가 담당하는 것이고 프로그램에서 직접적으로 해당장치에 연결되는 프로그램을 작성할 필요가 없습니다.

자바 프로그램의 경우 입출력 장치의 종류와 상관없이 해당 장치와 연결되는 스트림만 확보한다면 동일한 방식으로 입출력 프로그램을 작성할 수 있습니다.

2) 자바 IO

자바에서는 앞에서 언급한것 처럼 특정 장치와의 연결을 직접 담당하지 않고 스트림을 통해 입출력을 처리 합니다. 기본적으로 java.io 패키지의 클래스들을 사용합니다. 전통적으로 자바의 기본 입출력 프로그래밍 방법이며 연결되는 클라이언트가 적고 대용량의 데이터를 순차적으로 처리하는 유형의 프로그램에 적합 합니다.

[그림: 자바 스트림 개념]

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

스트림은 데이터 전달 방식에 따라 바이트 스트림과 문자 스트림으로 나뉩니다.

바이트 스트림(Byte Stream)

[그림: 바이트 스트림 클래스]

문자 스트림(Character Stream)

[그림: 문자 스트림 클래스]

보조 스트림

보조 스트림은 FilterInputStreamFilterOutputStream 을 상속받는 클래스들로 기본 스트림과 결합하여 특정 상황에서 보다 편리하게 사용할 수 있습니다.

다양한 스트림 클래스중 사용 목적에 맞는 적절한 클래스를 잘 선택해서 사용하는 것이 중요 합니다.

자바 프로그램에서 입출력 스트림을 생성하는 방법은 다음과 같습니다.

InputStream in = System.in;
OutputStream out = System.out;

int input = in.read();
out.write(input);
out.close();

» 실습: 자바 스트림 기본 예제

실습개요

키보드와 콘솔을 사용한 기본 입출력 예제 입니다. 바이트 스트림의 기본적인 특징을 이해하기 위해 1바이트만 처리하는 경우와 여러 바이트를 처리하는 경우를 함께 살펴 봅니다.

소스코드

public class BasicIOTest {
	public static void main(String[] args) {
		InputStream in = System.in;
		OutputStream out = System.out;

		int input;
		try {
			System.out.print("## Input 1~3 character: ");
			// input 1byte from keyboard
			input = in.read();
			System.out.println("## 1byte read and print");
			System.out.println(input);
			out.write(input);
			//out.flush();

			/*
			// input 3byte from keyboard
			byte[] b = new byte[3];
			in.read(b);
			System.out.println("## 3byte read and print");
			out.write(b);
			*/
			out.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

실행결과

## Input 1~3 character: A
## 1byte read and print
65
A
## Input 1~3 character: 황
## 1byte read and print
237
�
## Input 1~3 character: ABC
## 3byte read and print
ABC
## Input 1~3 character: 홍
## 3byte read and print
홍

3) 자바 NIO

스트림 기반의 자바 IO는 구조적으로 다소 느리며 블로킹 기반으로 동시 작업을 위해서는 스레드를 생성해야 하는데 대규모의 클라이언트 접속이 이루어지는 서비스에서는 스레드로 인한 부하가 발생해 성능에 문제가 발생하게 됩니다.

자바 NIO는 New IO 혹은 Non-blocking IO 라는 의미를 가지며 비동기 넌블로킹 처리가 가능 합니다. 스트림이 아닌 채널(Channel)을 사용하며 동시에 많은 접속이 이루어지며 단일 작업에 입출력 처리가 오래걸리지 않은 유형의 프로그램 개발에 적합 합니다.

버퍼(Buffer)

채널(Channel)

셀렉터(Selector)

NIO에서 사용하는 채널은 다음과 같이 4종류가 있습니다.

Path 를 이용해 파일을 다루는 방법은 다음과 같습니다.

Path path = Paths.get("/tmp/testfile.txt");
try(BufferedWriter writer = Files.newBufferedWriter(path, Charset.forName("UTF-8"))){
    writer.write("To be, or not to be. That is the question.");
}catch(IOException ex){
    ex.printStackTrace();
}

FileChannel 을 이용하는 경우 다음과 같이 구현할 수 있습니다.

FileInputStream fis = new FileInputStream("c:/tmp/testfile.txt");
FileChannel fileChannel2 = fis.getChannel();

ByteBuffer buffer = ByteBuffer.allocateDirect(data.length());
Charset charset = Charset.forName("UTF-8");

buffer = charset.encode("Hello World");
int byteCnt = fileChannel.write(buffer);
fileChannel.close();

파일 입출력과 관련된 보다 자세한 사항은 다음장에서 자세히 다루게 됩니다.



02: 파일 입출력

파일 입출력은 모든 입출력에서 가장 기본이 됩니다. 여기서는 자바에서 파일을 다루는 기본적인 사항들을 살펴보고 java.io 및 java.nio 패키지의 클래스들을 사용해 다양한 파일 입출력 프로그램 구현을 알아봅니다.

1) 파일과 디렉토리(File and Directory)

파일

파일은 컴퓨터에서 가장 기본이 되는 입출력 대상입니다.

디렉토리

디렉토리는 파일들을 관리하기 위한 구조로 일종의 트리(Tree)형태로 구성된 방(room)의 개념 입니다.

권한관리

대부분의 운영체제는 파일마다 권한 설정을 통해 일반 사용자와 시스템 관리를 위한 root 사용자를 구분하고 있습니다. 물론 하나의 운영체제를 여러명이 동시에 사용하는 유닉스 계열에서는 파일의 권한 관리는 매우 중요 합니다.

랜덤 엑세스(Random Access)

입출력은 기본적으로 순차적으로 처리되는것을 원칙으로 합니다. 따라서 파일의 경우에도 순차적으로 내용에 접근하게 되는데 파일의 경우 내용이 고정되어 있기 때문에 중간에 있는 데이터만 필요한 경우 특정 위치를 바로 접근할 방법이 필요하게 됩니다.

텍스트 파일과 바이너리 파일

파일의 내용이 일반 텍스트로 이루어진 파일을 텍스트 파일이라고 하며 이진 형식으로 이루어진 파일을 바이너리 파일이라고 합니다. 입출력시 파일의 내용에 따라 처리가 달라지므로 주의해야 합니다.

2) java.io 패키지를 이용한 파일 입출력

파일 자체는 File 클래스를 통해 핸들링 할 수 있으며 이를 통해 다양한 정보를 참조할 수 있습니다. File 클래스를 무조건 사용해야 하는 것은 아니며 FileReader 등의 입력 스트림을 통해서도 경로 기반으로 파일을 처리할 수 있습니다.

java.io.File

파일과 디렉토리 자체에 대한 정보를 제공하는 클래스로 파일이나 디렉토리 관리나 파일을 생성하는데 가장 기본이 되는 클래스 입니다.

기본적으로 제공하는 메서드 기능은 다음과 같습니다. java.nio.file 패키지에는 보다 다양한 파일 관련 클래스들이 있으며 향상된 기능을 제공하고 있습니다.

API 사용에 대한 자세한 사항은 자바 API 문서를 참고하기 바랍니다.

기본 사용법은 다음과 같습니다.


Java IO 파일 입출력

텍스트 파일인 경우 문자 스트림 클래스들을 사용하면 되고 바이너리 파일인 경우 바이트스트림을 기본적으로 사용합니다. 또한 입출력 효율을 위해 Buffered 계열의 보조 스트림을 함께 사용하는 것이 좋습니다.

정해진 경로를 기반으로 파일을 생성하고 파일로 부터 입력 스트림과 출력 스트림을 확보한 다음 읽기/쓰기 작업을 수행하면 됩니다.

텍스트 파일의 경우 다음과 같이 코드를 작성할 수 있습니다.

BufferedReader br = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));
String s;
while((s = br.readLine()) != null ) {
    bw.write(s+"\n");
}

이진 파일의 경우 바이트스트림을 사용해야 하며 다음과 같이 코드를 작성할 수 있습니다.

BufferedInputStream is = new BufferedInputStream(new FileInputStream("a.jpg"));
BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream("b.jpg"));

byte[] buffer = new byte[16384];
while (is.read(buffer) != -1) {
    os.write(buffer);
}

» 실습: 자바 IO 활용 예제

실습개요

java.io 패키지의 클래스를 이용해 간단한 메모장 프로그램을 만들어 봅니다.

소스코드

public class BinaryFileTest {
	public static void main(String[] args) {
		try {
			BufferedInputStream is = new BufferedInputStream(new FileInputStream("a.jpg"));
			BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream("b.jpg"));
			
			byte[] buffer = new byte[16384];
		    while (is.read(buffer) != -1) {
		        os.write(buffer);
		    }
		    is.close();
		    os.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

실행결과


참고 자료