본문 바로가기
JAVA

13. 입출력

by 김마리님 2020. 4. 6.

맨 처음 자바를 시작할 때 데이터는 0과 1로 이루어져있으며, on/off 경우의 수를 이용하여 데이터를 언급 했었다. 이런 신호를 디지털 신호라고 한다. 그러나 1,1같이 연속된 신호의 경우 긴 1로 인식되기 때문에 보레이트라는 것을 이용해서 시간 단위로 데이터를 끊는다. 따라서 보레이트가 잘게 쪼갤 수 있을 수록 단위시간동안 더 많은 데이터를 받을 수 있게 된다.

이 데이터의 흐름을 바이트 스트림이라고 한다. 그러나, 이 데이터는 끊어진 것이 아닌 끊임없이 흐르는 흐름이다. 이 흐름을 시간당 잡아내서 해석한다. 이 흐름을 끊고 연결해 줄 수 있게 하는 물질이 반도체이다.

영어는 8비트 스트림을 사용해왔는데, 8비트 스트림을 이용해서 전달받던 최초의 표를 아스키 코드라고 한다.

 

따라서 스트림이랑 응용프로그램과 입출력 장치를 연결하는 소프트웨어 모듈이다.

스트림은 두 가지가 있다.

1. 입력 스트림 : 입력 장치로부터 프로그램으로 데이터를 전달

2. 출력 스트림 : 출력 장치로 데이터 출력

스트림은 단방향으로 입력과 출력을 동시에 할 수 없다. 또한 단위에 따라도 스트림이 나뉘어진다.

1. 바이트 스트림 : 바이트 단위로 입출력되는 데이터를 처리.

2. 문자 스트림 : 문자만 전달하고 받는 스트림(2byte). 한국은 UTF-8로 3바이트로 처리함.

흐름은 다양한 노드를 거치는 수많은 경우의 수가 있다. 이 때, 트래픽 등으로 느려짐을 방지하기 위해 스트림은 항상 같은 흐름을 선택하지 않고 가장 빠르며 최적화된 흐름을 선택한다. 따라서 스트림은 논리적인 것이다. 

 

데이터를 보내는 각자의 흐름 속도가 다르기 때문에, 데이터는 순서대로 받는다는 보장성이 없다. 따라서 데이터의 위에 헤더를 붙여 보낸다. 이 때 헤더에 송신자의 이름 + 데이터 순서를 함께 기재해서 데이터를 입력한다. 이는 프로토콜로, 일종의 약속이다.

 

자바에서 데이터를 입력받는 형태를 보자,

package ch08;

import java.io.IOException;
import java.io.InputStream;

public class InputEx01 {
	public static void main(String[] args) {
		InputStream in = System.in;
		try {
			int data = in.read();
			System.out.println((char)data);
		} catch (IOException e) { 
			System.out.println("IO 오류가 발생");
			//e.getMessage(); 
			//e.printStackTrace();		
		}
	}
}

캐스팅 해주지 않으면 아스키코드가 출력되고, 글자 두 개 이상을 입력받을 수 없는 단점이 있다.

 

*try catch 구문은 입력받을 데이터보다 더 많이 들어오거나 하면 프로그램이 오류가 나기 때문에 예외처리를 해주어야 한다.

 

그래서 나오게 된 것이 배열단위로 데이터를 입출력하는 형태이다.

package ch08;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class InputEx02 {

	public static void main(String[] args) {
		InputStream in = System.in;
		InputStreamReader reader = new InputStreamReader(in);
		char[] data=new char[3];
		try {
			reader.read(data);
			System.out.println(data);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
	}

InputStreamReader은 캐스팅을 자동으로 도와주며, 배열을 통해 값을 1이상 넣을 수 있다.

그러나 이 방법도 배열의 크기가 정해져있는데다가, 만일 배열을 크게 만든다 하면 메모리 낭비가 생긴다

(ex - 1000개의 배열에 안녕하세요, 5개 적어 보내기)

따라서 데이터를 통신할 수 있는 형태가 가변적인 것이 요구된다.

이로 인해 나온 것이 버퍼(buffer)이다.

버퍼는 실시간 통신에서 자주 쓰이는 것으로, 배열처럼 주어진 크기 이상의 데이터를 버리는 것이 아니라 대기열에 올려둔다.

버퍼는 input 버퍼와 output 버퍼로 나뉘는데, input 버퍼가 가득 차면 output 버퍼로 보내면서 input 버퍼 내부의 데이터를 버리게 된다. 이를 플러시라고 한다. 물론 데이터가 다 차지 않아도 강제로 output으로 강제플러시도 가능하다.

output 버퍼로 들어온 데이터는 read 되는데, read된 데이터는 다시 플러시 되면서 데이터가 소멸된다.

만일 버퍼 크기보다 많이 받을 경우, 먼저 들어갈 수 있을 만큼의 데이터가 들어가서 오토 플러시가 일어난다. 오토 플러시가 끝나면 output 버퍼에 채워지고, 남은 데이터가 input 버퍼에 채워진다. output 데이터가 read 되어 플러시 되면 input 데이터가 플러시 된다.

 

이를 가변적으로 도와주는 클래스가 BufferReader과 BufferWriter이다.

package ch08;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class InputEx03 {
	public static void main(String[] args) {
		InputStream in = System.in;
		InputStreamReader reader = new InputStreamReader(in);

		BufferedReader br = new BufferedReader(reader); 

		BufferedReader br2 = new BufferedReader(new InputStreamReader(System.in));
		String data = "";
		try {
			while ((data=br2.readLine())!=null) {
				System.out.println(data);

		} catch (IOException e) {
			e.printStackTrace();
		}

	}
}

이 코드는 InputStreamReader을 통해 데이터를 입력 받고, BufferReader을 통해 입력받은 데이터를 출력할 수 있다.

버퍼를 이용하기 때문에 데이터의 크기에 제약을 받지 않게 된다. 자바가 입력받을 수 있는 최대 데이터는 8192 바이트로, 한국어는 2700글자까지 한번에 읽어들일 수 있으나, 버퍼가 있기 때문에 2700 이상의 데이터를 읽어들일 수 있다.

 

* while문을 통해 입력 한번만 받는 것이 아닌, 프로그램이 중지될 때 까지 돌아가는 입출력 프로그램을 만들 수 있다. 이 때, (data=br2.readLine())!=null이 아닌, true로 조건절을 연결하면 프로그램이 중지되지 않고 영원히 도는 프로그램을 만들 수 있다.

만일,

while (br2.readLine()!=null) {

data=br2.readLine();

System.out.println(data);

으로 연결하면 readline이 두번 성립하게 되어, readline으로 검증을 거치게 된다. 이런 데이터는 위험하기 때문에 readLine에서 읽어들인 데이터를 먼저 변수에 넣는, 우선순위에 변화를 줌으로써 readLine으로 검증을 하는 위험요소를 피할 수 있다.

 

반응형

'JAVA' 카테고리의 다른 글

웹과 OSI 7계층  (0) 2020.04.07
14. Json  (0) 2020.04.06
13. 접근지정자  (0) 2020.04.06
JAVA 실습 8. 객체 지향, 제네릭과 컬렉션을 이용한 커피숍 만들기.  (0) 2020.04.03
Warpper 클래스와 제네릭 기초  (0) 2020.04.03