막연하게만 사용하던 I/O를 조금 더 이해해보고자 작성한 글입니다.
기본적으로 I/O는 O/S 레벨에서 이루어지고 byte를 다룬다는 것을 생각야해합니다.
글 작성에 참고한 영상입니다.
Java IO - Input Streams by Kody Simpson
기본적으로 들어온 입력을 byte로 변환해주는 역할을 한다.
System.in의 read 메소드를 사용해서 콘솔에서 받은 입력을 어떻게 보여주는지 확인해보자.
입력된 스트림에서 1byte를 읽어서 int로 된 결과를 반환한다.
class TestInputStream {
public static void main(String[] args) {
InputStream in = System.in;
int data = in.read();
System.out.println(data);
}
}
위 코드는 아래와 같이 실행된다.
class TestInputStream {
public static void main(String[] args) {
InputStream in = System.in;
// 1. 콘솔에서 a를 입력받는다.
// a를 설정된 인코딩 방식을 적용해서 byte로 변경한다.
// 해당 byte를 int 형식으로 반환한다.
// a => U+0061 => 01100001
int data = in.read();
// 2. 97을 콘솔에 보여준다.
// 01100001은 10진수로 97
System.out.println(data);
}
}
인코딩
컴퓨터는 문자를 2진수로 구성된 byte로 저장해야한다.
문자를 어떻게 byte형식으로 변경할 것인가에 정의가 인코딩
UTF-8, UTF-16 등의 방식이 있다.[ UTF-8 ]
문자를 1byte ~ 4byte까지를 가지는 데이터로 변환하는 방식.
1) byte로 변경해야할 문자를 Unicode에서 code point를 찾는다.
2) Unicode의 위치에 따라서 1byte ~ 4byte사이의 길이가 결정된다.
3) 16진수인 code point를 2진수로 변경하여 아래의 방법으로 저장한다.
3) 저장되는 byte형식
- 1byte 문자는 0xxxxxxx 으로 구성된다 ( 0 + 7bit ascii )
- 2byte이상은 첫번째 byte는 110으로 시작되고, 나머지는 10으로 시작된다.
class TestInputStream {
public static void main(String[] args) {
InputStream in = System.in;
int data = in.read(); // 한글 '참' 입력
System.out.println(data);
}
}
위 코드는 아래와 같이 실행된다.
class TestInputStream {
public static void main(String[] args) {
InputStream in = System.in;
// 1. 콘솔에서 '참'를 입력받는다.
// '참'을 설정된 인코딩 방식을 적용해서 byte로 변경한다.
// '참'은 3bytes로 변경되어 입력버퍼에 저장된다.
// read()는 입력버터에서 1byte만 읽어온다.
// 11101100
int data = in.read();
// 2. 236을 콘솔에 보여준다.
// 11101100은 10진수로 236
System.out.println(data);
}
}
'참'을 화면에서 보려면 아래와 같이 실행할 수 있다.
class TestInputStream {
public static void main(String[] args) {
InputStream in = System.in;
OutputStream out = System.out;
int[] intArr = new int[3];
System.out.println("1: " + in.available()); // 0 - 스트림 길이
intArr[0] = in.read(); // '참' + 엔터 입력
// '참'의 첫번째 바이트 read
System.out.println("2: " + in.available()); // 3
intArr[1] = in.read(); // '참'의 두번째 바이트 read
System.out.println("3: " + in.available()); // 2
intArr[2] = in.read(); // '참'의 세번째 바이트 read
System.out.println("4: " + in.available()); // 1 - 엔터로 인한 뉴라인 존재
for (int i = 0; i < intArr.length; i++) {
out.write(intArr[i]); // '참'의 1 ~ 3번째 바이트 write
}
out.flush(); // '참' 출력
}
}
read()의 인자로 byte[ ]을 전달하면 버퍼로 사용할 수 있다.
read(b)의 결과값은 byte[ ]에 들어간 데이터의 길이를 반환한다.
public static void main(String[] args) throws IOException {
InputStream in = System.in;
OutputStream out = System.out;
byte[] b = new byte[8];
int data = 0;
while((data = in.read(b)) != -1) {
out.write(b, 0, data);
}
}
// 풀어서 쓰면
public static void main(String[] args) throws IOException {
InputStream in = System.in;
OutputStream out = System.out;
byte[] b = new byte[8];
int data = 0;
boolean isOn = true;
while(isOn) {
data = in.read(b);
System.out.println("\n data: " + data); // 읽어들인 데이터의 길이 or -1
if (data != -1) {
out.write(b, 0, data);
} else {
isOn = false;
}
}
}