자바 입출력(IO) 스트림

JONGCHAN SEO·2024년 8월 10일

자바 기본

목록 보기
5/10

자바 입출력(IO) 스트림의 기본 개념

자바에서 입출력(IO)스트림은 데이터를 읽고 쓰는 행위를 추상화한 것이다. 스트림은 데이터의 흐름을 나타내며, 자바에서는 java.io 패키지를 통해 다양한 입출력 관련 클래스를 제공한다.

스트림은 기본적으로 단방향 통신을 지원한다. 즉, 입력 스트림은 데이터를 읽기만 할 수 있고, 출력 스트림은 데이터를 쓰기만 할 수 있다. 왜냐하면 스트림의 추상화된 모델이 데이터의 단방향 흐름을 가정하기 때문이다.

자바의 IO스트림은 바이트 기반 스트림문자 기반 스트림으로 나뉜다.
바이트 기반 스트림은 이미지,동영상 파일 등 비텍스트 데이터를 처리할 때 사용 되며, 문자 기반 스트림은 텍스트 데이터를 처리할 때 사용된다.

🍿스트림을 사용할 때는 항상 스트림을 닫아주는 것이 중요하다.
닫지 않으면 메모리 누수와 같은 리소스 관련 문제가 발생할 수 있다.
왜냐하면 열린 스트림은 시스템 리소르를 계속해서 점유하기 때문

⛵Stream 활용 : InputStream / OutputStream

자바에서는 처리 단위에 따라Reader - inputStream / Writer - outputStream 으로 나뉘어 통신한다.
입력 스트림 inputStream은 스트림을 한 줄씩 읽고, 출력 스트림 outputStream은 데이터를 내보내며 해당 공간을 비운다.

inputStream/outputStream은 다음과 같이 사용할 수 있다.

import java.io.InputStream;
import java.io.OutputStream;

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

// input은 read와 연결되어 있기 때문에 in.read를 사용한다.
int idata = in.read();

// output은 write와 연결되어 있기 때문에 out.write를 사용한다.
out.wrte(idata);
out.flush(); // flush를 써주지 않으면 출력되지 않는다.
out.close() // output을 끝내는 메서드

-- 이때 중요한 것은 다음과 같다.
inputStream으로 받아오는 경우 여러 개의 값을 입력해도 단 1개의 문자밖에 못 가져오며 기본형은 int로 받아온다.

🏜️InputStreamReader / OutputStreamWriter

앞서 InputStream/OutputStream을 사용했다. 하지만 이것들은 단 하나의 값밖에 입력받지 못하고, 하나밖에 출력하지 못한다.
그렇다면 여러개의 값을 입출력하기 위한 방법은 뭘까?라 하면
InputStreamReader/OutputStreamWriter다.

import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

//InputStreamReader 로 입력받는 경우에는 
// 배열을 어떻게 주느냐에 따라 2개 이상의 값을 받을 수 있음
InputStream in = System.in;
InputStreamReader reader = new InputStreamReader(in); 
// InputStreamReader 사용하기 위해 객체 생성
		
OutputStream out = System.out;
OutputStreamWriter writer = new OutputStreamWriter(out);
// OutputStreamWriter 사용하기 위해 객체 생성
		
char cdata[] = new char[2];
// 이제는 char 를 기본형으로 받을 수 있고, 2개 이상의 값을 배열을 통해 받아올수 있다.
reader.read(cdata);
		
int IcData = cdata[0]-'0'; 
// 배열이기 때문에 char 로 받은 값을 int로 변환하여 계산하고 싶은 경우 이처럼 사용해야한다.
		
writer.write("입력받은 값 : ");
writer.write(cdata);
writer.write("\n");
writer.write("입력받은 첫번째 값 + 10 : ");
writer.write(IcData+10+"\n"); // 입력받은 첫번째 값 +10
		
System.out.println("#######결과#######");
writer.flush(); // 이 매서드를 통해 출력
writer.close();

-- 여기서 중요한 것은 다음과 같다.
기존 inputStream에서 1개의 값밖에 가져올 수 없던 부분은 InputStreamReader로 보완할수 있으며, 2개 이상의 값을 받아오기 위해서는 배열을 사용해서 값을 받아와야 한다.
즉, 고정되어있는 값 밖에 받아올 수 없다.

내가 입력하는 값이 고정되어있는 값보다 작다면 그만큼 공간의 낭비가 생기게 되고, 고정되어 있는 값보다 크다면 공간이 부족해지는 문제가 발생한다. == 효율이 나쁘다.

🍔버퍼(Buffer)란 무엇인가

버퍼는 InputStream과 InputStreamReader를 보완하고 합쳐 탄생한 입출력의 최종형태의 느낌이다.

먼저 버퍼는 고정값이 아니라 가변적인 값을 받게 된다.
원하는 크기에 따라 받는다는것이다.
동시에 버퍼는 입력받은 값을 버퍼에 저장해두었다가 버퍼가 가득차거나 개행 문자가 나타나면 버퍼의 내용을 한 번에 전송하게 된다.(중요)

버퍼의 큰 장점 중 하나는 한 번에 전송하는 방법을 통해 속도가 엄청나게 빨라진다. 입력받는 값이 많으면 많을수록 Buffer를 사용하여 데이터를 입출력 하는게 Scanner를 통해 하나하나 출력하는것보다 훨씬 빠르다.

당연히 단점도 있다.
1. 코드가 복잡해보일 수 있다.(가독성 문제)
2. 띄어쓰기(스페이스)와 엔터(개행문자)를 경계로 입력 값을 인식하는 Scanner와 달리 BufferReader는 엔터만 경계로 인식하기 떄문에 중간에 띄어쓰기라도 하는 경우 데이터를 가공 해주어야 한다.

그럼 버퍼를 어떻게 써야할까?

-- Buffer 객체 생성 시에는 InputStream과 InputStreamReader의 합쳐진 형태를 취한다.
-- Buffer 사용시에는 java.io.BufferedReader,Writer 임포트 뿐만 아니라 반드시 메인 메소드에 IOException으로 예외처리를 해주어야 한다.

import java.io.BufferedReader; // 임포트 필수
import java.io.BufferedWriter;

public static void main(String[] args) throws IOException {
// 여기도 필수! 아니면 try ~ catch 사용해야함

	InputStream in = System.in;	
	InputStreamReader reader = new InputStreamReader(in);
		
	OutputStream out = System.out;
	OutputStreamWriter writer = new OutputStreamWriter(out);

	// 위의 4줄이 아래의 하나의 줄로 줄어든다.
	BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
 }

다음은 입출력 하는 방법이다.

import java.io.BufferedReader; // 임포트 필수
import java.io.BufferedWriter;

public static void main(String[] args) throws IOException {

	BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

	String s = br.readLine(); // bufferedwriter 의 기본형은 String
		
	int i = Integer.parseInt(s) +10; // String 을 int로 형변환 후 +10 !!
		
	br.close(); // bufferedreader 도 입력을 마쳤다면 닫아주자
		
	bw.write("입력받은 값 : "+ s); // 출력
	bw.newLine(); // 개행 메소드
	bw.write("입력받은 값 +10 : "+i+"\n"); // 이렇게 하니까 제대로 출력됨

	bw.flush(); // 남은 값 출력 && 버퍼 초기화
	bw.close(); // bufferedwriter 닫기
 }

Buffer에 대한 정리
1. Buffer는 가변적인 값을 받을 수 있으며, 입력받은 값을 Buffer에 저장하고, Buffer에 저장한 값을 한 번에 출력하기 때문에 속도가 빠르다.
2. Buffer의 기본 타입은 String이며, 엔터를 경계로 값을 인식한다.
3. BufferedWriter를 사용해서 입력되었던 것들을 출력한다. 이후 flush()를 사용하게 된다. Buffer는 기본적으로 버퍼가 꽉 차기 전에는 출력되지 않는데, flush()를 통해 꽉차지 않아도 buffer 내용을(강제로) 출력 후 버퍼를 비우게 된다.

BufferedReader : 자주 쓰는 메소드

타입/메소드 내용 설명
void Close() : Closes the stream and releases any system resources associated with it. 입력 스트림을 닫고 사용하던 자원들을 푼다
boolean markSupported() : Tells whether this stream supports the mark() operation, which it does.스트림이 mark 기능을 지원하는지 true / false 로 알려준다
String readLine() : Reads a line of text.String 타입으로 한 줄을 읽어온다
void reset() Resets the stream to the most recent mark.마킹이 있으면 그 위치부터 다시 시작, 그렇지 않다면 처음부터 다시 시작!

BufferdWriter : 자주 쓰는 메소드

타입/메소드 내용 설명
void flush() : Flushes the stream.버퍼에 남은 값 출력 && 버퍼 초기화(비우기)
void newLine() : Writes a line separator.버퍼에서 사용하는 개행 메소드
profile
잘 배워가겠습니다.

0개의 댓글