데이터를 읽고 출력하기 위해 사용되는 입출력 API(Application Programming Interface, API는 라이브러리 라고 부르기도 한다.)에 대해 알아보았다.
자바에서 데이터는 스트림(Stream)을 통해 입출력된다. 스트림은 단일 방향으로 연속적으로 흘러가는 것을 말하는데, 물이 높은 곳에서 낮은 곳으로 흐르듯이 데이터는 출발지에서 도착지로 흘러간다.
프로그램이 도착지이면 흘러온 데이터를 입력받아야 하므로 입력 스트림을 사용하고, 반대로 프로그램이 출발지면 데이터를 출력해야 하므로 출력 스트림을 사용한다.
java.io 패키지에는 여러 가지 종류의 스트림(Stream) 클래스를 제공하고 있다.
스트림 클래스는 다음과 같이 크게 두 종류로 구분된다.
바이트(byte) 기반 스트림
그림, 멀티미디어 등의 바이너리 데이터를 읽고 출력할 때 사용
최상위 클래스
문자(character) 기반 스트림
문자 데이터를 읽고 출력할 때 사용
최상위 클래스
스트림 클래스가 바이트 기반인지, 문자 기반인지를 구별하려면 최상위 클래스를 보면 된다.
바이트 기반 입출력 스트림의 하위(자식) 클래스는 모두 바이트 기반 입출력 스트림이며, 클래스 접미사로 InputStream 또는 OutputStream이 붙는다.
문자 기반 입출력 스트림의 하위(자식) 클래스는 모두 문자 기반 입출력 스트림이며, 클래스 접미사로 Reader 또는 Writer가 붙는다.
OutputStream은 바이트 기반 출력 스트림의 최상위 클래스로 추상 클래스이다.
import java.io.OutputStream;
을 선언해줘야 사용할 수 있다.
다음은 OutputStream 클래스의 주요 메소드이다.
리턴 타입 | 메소드 | 설명 |
---|---|---|
void | write(int b) | 1byte를 출력한다. |
void | write(byte[] b) | 매개값으로 주어진 배열 b의 모든 바이트를 출력한다. |
void | write(byte[] b, int off, int len) | 매개값으로 주어진 배열 b[off] 부터 len개까지의 바이트를 출력한다. |
void | flush() | 출력 버퍼에 잔류하는 모든 바이트를 출력한다. |
void | close() | 출력 스트림을 닫는다. |
출력을 위해서는 write() 후 flush()와 close()를 모두 사용해주어야한다.
flush()는 write()에 저장된 값을 출력함과 동시에 비워주는 역할이고, close()는 끝 마무리해주는 역할이다.
정보를 출력할 때 OutputStream은 System.out
을 사용한다.
OutputStream out = System.out;
만약 파일 출력 스트림을 생성하고 사용하면 다음과 같이 작성해준다.
import java.io.FileOutputStream;
... (생략) ...
OutputStream out = new FileOutputStream("파일 경로");
InputStream은 바이트 기반 입력 스트림의 최상위 클래스로 추상 클래스이다.
import java.io.InputStream;
을 선언해줘야 사용할 수 있다.
다음은 InputStream 클래스의 주요 메소드이다.
리턴 타입 | 메소드 | 설명 |
---|---|---|
int | read() | 1byte를 읽고 읽은 바이트를 리턴한다. |
int | read(byte[] b) | 읽은 바이트를매개값으로 주어진 배열에 저장하고 읽은 바이트 수를 리턴한다. |
int | read(byte[] b, int off, int len) | len개의 바이트를 읽고 매개값으로 주어진 b[off]부터 len개까지 저장한다. 그리고 읽은 바이트 수를 리턴한다. |
void | close() | 입력 스트림을 닫는다. |
정보를 입력할 때 InputStream은 System.in
을 사용한다.
InputStream in = System.in
만약 파일 입력 스트림을 생성하고 사용하면 다음과 같이 작성해준다.
import java.io.FileInputStream;
... (생략) ...
InputStream in = new FileInputStream("파일 경로");
Writer는 문자 기반 출력 스트림의 최상위 클래스로 추상 클래스이다.
import java.io.Writer;
를 선언해줘야 사용할 수 있다.
다음은 Write 클래스의 주요 메소드이다.
리턴 타입 | 메소드 | 설명 |
---|---|---|
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() | 출력 스트림을 닫는다. |
Reader는 문자 기반 입력 스트림의 최상위 클래스로 추상 클래스이다.
import java.io.Reader;
를 선언해줘야 사용할 수 있다.
다음은 Reader 클래스의 주요 메소드이다.
리턴 타입 | 메소드 | 설명 |
---|---|---|
int | read() | 1개의 문자를 읽고 리턴한다. |
int | read(char[] cbuf) | 읽은 문자들을 매개값으로 주어진 문자 배열에 저장하고 읽은 문자 수를 리턴한다. |
int | read(char[] cbuf, int off, int len) | len개 문자를 읽고 매개값으로 주어진 문자 배열에서 cbuf[off]부터 len개까지 저장한다. 그리고 읽은 문자 수를 리턴한다. |
void | close() | 입력 스트림을 닫는다. |
모든 입출력이 끝나고 난 후, 입출력 스트림을 사용하지 않겠다는 의미로 close() 메소드를 호출해줘야 한다.
보조 스트림이란 다른 스트림과 연결이 되어 여러 가지 편리한 기능을 제공해주는 스트림을 말한다.
보조 스트림은 자체적으로 입출력을 수행할 수 없기 때문에 입출력 소스와 바로 연결되는 InputStream, OutputStream, Reader, Writer 등에 연결해서 입출력을 수행한다.
보조 스트림은 문자 변환, 입출력 성능 향상, 기본 타입 입출력 등의 기능을 제공한다.
보조 스트림을 연결하려면 보조 스트림을 생성할 때 자신이 연결될 스트림을 다음과 같이 생성자의 매개값으로 제공하면 된다.
보조스트림 변수 = new 보조스트림(연결스트림)
예를 들어 InputStream을 문자 변환 보조 스트림인 InputStreamReader에 연결하는 코드는 다음과 같다.
InputStream is = ''';
InputStreamReader reader = new InputStreamReader(is);
또는 보조 스트림을 연속적으로 연결할 수 있다.
예를 들어 문자 변환 보조 스트림인 InputStreamReader를 다시 성능 향상 보조 스트림인 BufferedReader에 연결할 수 있다.
InputStream is = System.in;
InputStreamReader reader = new InputStreamReader(is);
BufferedReader br = new BufferedReader(reader);
프로그램의 실행 성능은 입출력이 가장 늦은 장치를 따라간다. CPU와 메모리가 아무리 뛰어나도 하드 디스크의 입출력이 늦어지면 프로그램의 실행 성능은 하드 디스크의 처리 속도에 맞춰진다.
이 문제를 해결하기 위해 프로그램이 입출력 소스와 직접 작업하지 않고 중간에 메모리 버퍼
와 작업함으로써 실행 성능을 향상시킬 수 있다.
버퍼는 데이터가 쌓이기를 기다렸다가 꽉 자체 되면 데이터를 한꺼번에 하드 디스크로 보냄으로써 출력 횟수를 줄여준다.
기본적으로 출력 스트림은 내부에 작은 버퍼를 가지고 있다. 하지만 이것만으로는 불충분하다.
보조 스트림 중에서는 위와 같이 메모리 버퍼를 추가로 제공하여 프로그램의 실행 성능을 향상 시키는 것들이 있다.
바이트 기반 스트림에서는 BufferedInputStream, BufferedOutputStream이 있다.
문자 기반 스트림에서는 BufferedReader, BufferedWriter이 있다.
BufferedOutputStream은 바이트 기반 출력 스트림에 연결되어 버퍼를 제공해주는 보조 스트림이고, BufferedWriter는 문자 기반 출력 스트림에 연결되어 버퍼를 제공해주는 보조 스트림이다.
프로그램에서 전송한 데이터를 내부 버퍼에 쌓아 두었다가 버퍼가 꽉 차면, 버퍼의 모든 데이터를 한꺼번에 보낸다.
프로그램 입장에서 보면 메모리 버퍼로 데이터를 고속 전송하기 때문에 출력 성능이 향상되는 효과를 얻게 된다.
BufferedOutputStream과 BufferedWriter 보조 스트림은 다음과 같이 생성자의 매개값으로 준 출력 스트림과 연결되어 추가적인 내부 버퍼를 제공한다.
BufferedOutputStream bos = new BufferedOutputStream(바이트 기반 출력 스트림);
BufferedWriter bw = new BufferedWriter(문자 기반 출력 스트림);
BufferedInputStream은 바이트 기반 입력 스트림에 연결되어 버퍼를 제공해주는 보조 스트림이고, BufferedReader는 문자 기반 입력 스트림에 연결되어 버퍼를 제공해주는 보조 스트림이다.
입력 소스로부터 자신의 내부 버퍼 크기 만큼 데이터를 미리 읽고 버퍼에 저장해둔다.
프로그램은 외부의 입력 소스로부터 직접 읽는 대신 버퍼로부터 읽음으로써 읽기 성능이 향상된다.
BufferedInputStream과 BufferedReader 보조 스트림은 다음과 같이 생성자의 매개값으로 준 입력 스트림과 연결되어 추가적인 내부 버퍼를 제공한다.
BufferedInputStream bis = new BufferedInputStream(바이트 기반 입력 스트림);
BufferedReader br = new BufferedReader(문자 기반 입력 스트림);
DataInputStream과 DataOutputStream 보조 스트림을 연결하면 기본 타입인 boolean, char, short, int, long, float, double을 입출력할 수 있다.
다음은 DataInputStream과 DataOutputStream 객체를 생성하는 코드이다. 다른 보조 스트림과 마찬가지로 연결할 바이트 입출력 스트림을 생성자의 매개값으로 주면 된다.
DataInputStream dis = new DataInputStream(바이트 기반 입력 스트림);
DataOutputStream dos = new DataOutputStream(바이트 기반 출력 스트림)'
다음은 기본 타입을 입출력하기 위해 DataInputStream과 DataOutputStream이 제공하는 메소드를 보여준다.
DataInputStream | DataOutputStream | ||
---|---|---|---|
boolean | readBoolean | void | writeBoolean(boolean v) |
byte | readByte() | void | writeByte(int v) |
char | readChar() | void | writeChar(int v) |
double | readDouble() | void | writeDouble(double v) |
float | readFloat() | void | writeFloat(float v) |
int | readInt() | void | writeInt(int v) |
long | readLong() | void | writeLong(long v) |
short | readShort() | void | writeShort(int v) |
String | readUTF() | void | writeUTF(String str) |
이 메소드로 입출력할 때 주의할 점이 있는데, 데이터 타입의 크기가 모두 다르므로 DataOutputStream으로 출력한 데이터를 다시 DataInputStream으로 읽어올 때는 출력한 순서와 동일한 순서로 읽어야 한다.
예를 들어 출력할 때의 순서가 int -> boolean -> double 이라면 읽을 때의 순서도 int -> boolean -> double 이어야 한다.
콘솔로부터 데이터를 입력받을 때 System.in을 사용하고, 콘솔에 데이터를 출력할 때 System.out을 사용한다. 그리고 에러를 출력할 때에는 System.err를 사용한다.
콘솔에서 키보드의 데이터를 입력받을 수 있도록 System 클래스의 in 정적 필드를 제공한다.
System.in은 InputStream 타입의 필드이므로 InputStream 변수로 참조가 가능하다.
InputStream is = System.in;
Reader reader = new InputStreamReader(is);
BufferedReader br = new BufferedReader(reader);
String lineStr = br.readLine();
InputStreamReader 보조 스트림을 연결해서 Reader로 변환한다. 그리고 라인 단위로 읽기 위해 BufferedReader 보조 스트림을 추가로 연결한다.
그러면 readLine() 메소드소 입력된 라인을 읽을 수 있다.
콘솔에서 모니터로 데이터를 출력하기 위해서는 System 클래스의 out 정적 필드를 사용한다.
System.out은 PrintStream 타입의 필드이다.
따라서 PrintStream이 제공하는 print(), println(), printf()와 같은 메소드를 이용해서 모니터로 출력할 수 있다.
Scanner 클래스는 입출력 스트림도 아니고, 보조 스트림도 아니다.
Scanner는 문자 파일이나 바이트 기반 입력 스트림에서 라인 단위 문자열을 쉽게 읽도록 하기 위해 java.util 패키지에서 제공하는 클래스이다.
Scanner는 라인 단위 문자열을 읽기 위해 nextLine()
메소드를 제공한다.
java.io 패키지에서 제공하는 File 클래스는 파일 및 폴더(디렉토리) 정보를 제공해주는 역할을 한다.
File 클래스로부터 File 객체를 생성하려면 문자열 경로를 다음과 같이 제공해야 한다.
File file = new File(파일 경로); // 파일 생성
boolean isExist = file.exists(); // 파일이 생성 되었는지 확인
파일이 생성되었는지 확인하고 싶다면, File 객체를 생성하고 나서 exists() 메소드를 호출해보면 된다. 파일이나 폴더가 존재하면 true를 리턴하고 그렇지 않다면 false를 리턴한다.
exists() 메소드의 리턴값이 true라면 다음 메소드를 사용할 수 있다.
리턴 타입 | 메소드 | 설명 |
---|---|---|
boolean | delete() | 파일 또는 폴더를 삭제한다. |
boolean | canExecute() | 실행할 수 있는 파일인지 여부를 확인한다. |
boolean | canRead() | 읽을 수 있는 파일인지 여부를 확인한다. |
boolean | canWrite() | 수정 및 저장할 수 있는 파일인지 여부를 확인한다. |
String | getName() | 파일의 이름을 리턴한다. |
String | getParent() | 부모 폴더를 리턴한다. |
File | getParentFile() | 부모 폴더를 File 객체로 생성 후 리턴한다. |
String | getPath() | 전체 경로를 리턴한다. |
boolean | isDirectory() | 폴더인지 여부를 확인한다. |
boolean | isFile() | 파일인지 여부를 확인한다. |
boolean | isHidden() | 숨김 파일인지 여부를 확인한다. |
long | lastModified() | 마지막 수정 날짜 및 시간을 리턴한다. |
long | length() | 파일의 크기를 리턴한다. |
String[] | list() | 폴더에 포함된 파일 및 서브 폴더 목록 전부를 String 배열로 리턴한다. |
String[] | list(FilenameFilter filter) | 폴더에 포함된 파일 및 서브 폴더 목록 중에 FilenameFilter에 맞는 것만 String 배열로 리턴한다. |
File[] | listFiles() | 폴더에 포함된 파일 및 서브 폴더 목록 전부를 File 배열로 리턴한다. |
File[] | listFiles(FilenameFilter filter) | 폴더에 포함된 파일 및 서브 목록 중에 FilenameFilter에 맞는 것만 File 배열로 리턴한다. |