Lesson: Basic I/O
- basic I/O를 위해 사용하는 자바 클래스들에 대해 배우기
- I/O 스트림 : I/O 연산을 매우 간단하게 해주는 강력한 개념
- Serialization : 프로그램이 전체 객체를 stream에 쓰고 읽게 해준다.
- file I/O & file system 연산
I/O Streams
- I/O Stream은 입력 src 또는 출력 dest를 뜻한다.
- stream은 많은 종류의 src와 dest를 표현한다.
- disk files, devices, other programs, memory arrays
- stream은 여러 종류의 데이터를 지원한다.
- bytes, primitive data type, localized character, object
- 몇몇의 stream은 data를 보내고 몇몇은 데이터를 유용한 방법으로 변환 하고 전송한다.
- 내부적으로 어떻게 작동하던지, 모든 스트림은 같은 단순한 모델을 그 스트림을 사용하는 프로그램에게 보여준다.
- 스트림은 데이터의 연속이다.
- input stream을 사용하는 프로그램은 소스로 부터 데이터를 한번에 하나의 item씩 읽는다.

- output stream을 사용하는 프로그램은 데이터를 한번에 하나의 item씩 목적지에 쓴다.

- 이번 레슨에서 primitive 변수 부터 advanced object들의 모든 종류를 다루는 스트림을 알아본다.
- 위 사진에서 데이터 소스와 데이터 목적지는 데이터를 생성하거나 소비, 가지고 있는 어떠한 것도 가능하다.
- disk files, program, peripheral device, network socket, array 등
- 다음 섹션에서 가장 기본적인 stream인 byte stream을 사용해 Stream I/O의 기본적인 연산을 시연한다.
Byte Streams
- (8비트)바이트의 입력과 출력을 사용하기 위해 byte stream을 사용
- 모든 바이트 스트림 클래스들은 InputStream과 OutputStream으로부터 파생된다.
- 많은 바이트 스트림 클래스들이 존재한다.
- 바이트 스트림의 작동을 알아보기위해 여기선 file I/O 바이트 스트림인 FileInputStream 와 FileOutputStream에 집중한다.
- 다른 종류의 바이트 스트림들은 거의 같은 방법으로 사용된다.
- 주요 다른점은 생성되는 방식이다.
Using Byte Streams
public class CopyBytes {
public static void main(String[] args) throws IOException {
FileInputStream in = null;
FileOutputStream out = null;
try {
in = new FileInputStream("xanadu.txt");
out = new FileOutputStream("outagain.txt");
int c;
while ((c = in.read()) != -1) {
out.write(c);
}
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
}
}
- CopyBytes는 대부분의 시간을 input stream에서 읽고 output 스트림에서 쓰는(한번에 1 byte씩) 단순한 loop에서 보낸다.
Always Close Streams
- 필요하지 않은 스트림을 닫는것은 매우 중요하다.
- 너무도 중요한 나머지 에러가 발생한 경우에도 finally 블록을 통해 두 스트림을 닫는다.
- 이러한 연습은 심각한 리소스 유출을 막는다.
- 에러가 발생하는 하나의 예는 CopyBytes가 하나 또는 두 파일을 열 수 없는 경우이다.
- 이 경우 파일에 연결된 스트림 변수는 초기 null 값에서 변하지 않는다.
- 예에서 close를 호출하기 전에 각 스트림 변수가 객체 주소를 담고있는지 확인하는 이유이다.
When Not to Use Byte Streams
- CopyBytes는 평범한 프로그램같아 보이지만 사실 피해야할 low-level I/O이다.
- xanadu.txt 가 캐릭터 데이터를 포함하고 있기 때문에 가장 적절한 접근방법은 character stream을 사용하는 것이다.
- 바이트 스트림들은 가장 원시적인 I/O에서만 사용해야한다.
- 그럼에도 바이트 스트림에 대해 설명하는 이유는 모든 다른 스트림 타입들이 바이트 스트림으로 만들어졌기 때문이다.
Character Streams
- 자바 플랫폼은 유니코드 컨벤션을 사용해 문자 값을 저장한다.
- 문자 스트림 I/O는 자동적으로 이런 내부 포맷을 로컬 문자 셋으로/부터 변환한다.
- 서부 지역에서 로컬 문자 셋은 8비트의 ASCII 셋이다.
- 대부분의 어플리케이션에서 문자 스트림은 바이트스트림을 사용한 I/O보다 복잡하지 않다.
- 스트림 클래스를 통한 입력과 출력은자동적으로 로컬 문자 셋으로부터/으로 변환된다.
- 캐릭터 스트림을 사용하는 프로그램은 자동적으로 로컬 문자 셋으로 변환하고 국제화를 준비한다. ( 프로그래머의 추가 노력 없이)
- 만약 국제화가 중요하지 않다면 문자 셋 이슈에 집중하지 않고 단순히 문자 스트림 클래스를 사용하면 된다.
- 나중에 국제화가 중요해진다면 광범위한 레코딩없이 변환할 수 있다.
- 추가 정보 : https://docs.oracle.com/javase/tutorial/i18n/index.html
Using Character Streams
- 모든 문자 스트림 클래스들은 Reader 와 Writer로부터 파생된다.
- 바이트 스트림과 같이 파일 I/O에 특화된 문자 스트림 클래스가 존재한다.
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CopyCharacters {
public static void main(String[] args) throws IOException {
FileReader inputStream = null;
FileWriter outputStream = null;
try {
inputStream = new FileReader("xanadu.txt");
outputStream = new FileWriter("characteroutput.txt");
int c;
while ((c = inputStream.read()) != -1) {
outputStream.write(c);
}
} finally {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
}
}
- CopyBytes와 매우 흡사
- 가장 중요한 차이점은 FileInputStream and FileOutputStream 대신 FileReader and FileWriter을 사용
- 두 예제 모두 int 변수를 사용해 읽고 썻다.
- 다른점은 이 예제에서 int 변수는 문자 값을 마지막 16비트에 저장하고 바이트예제에선 마지막 8비트에 byte를 저장
Character Streams that Use Byte Streams
- 문자 스트림은 종종 바이트 스트림의 wrapper이다.
- 문자 스트림은 문자 스트림이 문자와 바이트간의 변환을 핸들하는 동안 바이트 스트림을 사용해 물리적 I/O를 수행한다.
- 예를 들어 FileReader는 FileInputStream을 사용하고 FileWriter는 FileOutputStream을 사용한다.
- 일반적 목적의 바이트to문자 다리역할을 하는 스트림이 존재한다.
- InputStreamReader 와 OutputStreamWriter
- 당신의 요구사항과 맞는 문자 스트림 클래스가 존재하지 않을 때문자 스트림을 생성하는데 사용
- 소켓 레슨에서 소캣 클래스가 제공하는 바이트 스트림으로부터 문자 스트림 생성 방법을 보여준다.
Line-Oriented I/O
- 문자 I/O는 하나의 문자가 아닌 큰 단위의 문자에서 발생한다.
- 흔한 단위는 줄이다.
- 문자들 + 마지막에 line terminator
- line terminator는 \r\n 또는 \r 또는 \n이 될 수 있다.
- 가능한 모든 줄 종결자를 지원함으로써 프로그램은 널리 사용되는 운영 체제에서 작성된 텍스트 파일을 읽을 수 있다.
- 2번째 예를 line-oriented I/O로 변환해보자
import java.io.FileReader;
import java.io.FileWriter;
import java.io.BufferedReader;
import java.io.PrintWriter;
import java.io.IOException;
public class CopyLines {
public static void main(String[] args) throws IOException {
BufferedReader inputStream = null;
PrintWriter outputStream = null;
try {
inputStream = new BufferedReader(new FileReader("xanadu.txt"));
outputStream = new PrintWriter(new FileWriter("characteroutput.txt"));
String l;
while ((l = inputStream.readLine()) != null) {
outputStream.println(l);
}
} finally {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
}
}
- BufferedReader and PrintWriter 사용
- readLine 호출은 하나의 text줄을 반환
- println을통해 각 줄을 출력
- 현재 운영체제에 맞는 줄 종결자를 추가한다.
- 추가된 줄 종결자는 입력 파일에서 사용하는 줄 종결자와 다를 수 있다.
- text 입력과 출력을 문자와 줄말고도 구성하는 여러 방법이 존재