BufferedReader / BufferedWriter
BufferedReader / BufferedWriter는 Buffer에 있는 IO 클래스이다.
입력된 데이터가 바로 전달되지 않고 중간에 버퍼링이 된 후에 전달된다.
출력도 마찬가지로 버퍼를 거쳐 간접적으로 출력장치에 전달되기 때문에 시스템의 데이터 처리 효율성을 높여주며 버퍼 스트림을 InputStreamReader / OutputStreamWriter 를 같이 사용하여 버퍼링을 하게 되면 입출력 스트림으로부터 미리 버퍼에 데이터를 갖다 놓기 때문에 보다 효율적인 입출력이 가능하다.
쉽게 말해 버퍼를 이용해서 입출력을 하는 메소드라는 것이다. Java를 공부한 지 얼마 안 된 저와 같은 사람들은 대부분 Scanner 클래스를 사용하여 입력을 받고 System.out.println( )을 이용해서 출력한다. 하지만 알고리즘 문제를 풀다 보면 더 빠른 입출력을 이용해야 할 때가 있다.
BufferReader가 Scanner보다 빠른 이유가 무엇일까?
바로 버퍼를 사용하기 때문
버퍼를 사용하면 한 번 더 거쳐가서 느릴 거 같은데 왜 빠를까?
하드디스크는 원래 속도가 엄청 느리다. 하드뿐만 아니라 키보드나 모니터와 같은 외부 장치와의 데이터 입출력은 생각보다 시간이 걸리는 작업이다. 버퍼링 없이 키보드가 눌릴 때마다 눌린 문자의 정보를 목적지로 바로 이동시키는 것보다 중간에 버퍼를 둬서 데이터를 한데 묶어서 이동시키는 것이 보다 효율적이고 빠르다.
(버퍼 없이 전송하게 되면 CPU와 성능 갭이 많이 나서 비효율적이다.)
버퍼에 대한 용어 정리를 해보자.
버퍼(buffer)
- 데이터를 한 곳에서 다른 한 곳으로 전송하는 동안 일시적으로 그 데이터를 보관하는 임시 메모리 영역
- 입출력 속도 향상을 위해 버퍼 사용
버퍼 플러시(buffer flush)
- 버퍼에 남아 있는 데이터를 출력시킴 (버퍼를 비우는 동작)
버퍼를 이용한 입력
- BufferedReader
버퍼를 이용한 출력
- BufferedWriter
BufferedReader
제가 처음 접한 입출력 방식인 Scanner를 통해 입력을 받을 경우 Space(띄어쓰기), Enter(줄바꿈, 개행) 모두를 경계로 인식하기에 입력받은 데이터를 가공하기 매우 편리하다. 하지만 그에 비해 BufferedReader는 Enter(줄바꿈, 개행)만 경계로 인식하고 받은 데이터가 String으로 고정되기 때문에 입력받은 데이터를 가공하는 작업이 필요할 경우가 많다.
Scanner에 비해 사용하기 다소 불편하다. 그러나 많은 양의 데이터를 입력 받을 경우 훨씬 더 효율적이고 작업 속도에서 많은 차이가 발생한다.
공식 문서에는
Reads text from a character-input stream, buffering characters so as to provide for the efficient reading of characters, arrays, and lines. The buffer size may be specified, or the default size may be used.
입력 스트림에서 문자를 읽는 함수인데 문자나 배열, 라인들을 효율적으로 읽기 위해서 문자들을 버퍼에 저장하고(버퍼링) 읽는 방법을 취한다. 버퍼 사이즈는 직접 지정할 수도 있지만 지정 안 할 경우에 기존 디폴트 사이즈가 사용된다.
라고 나와 있다.
추가적으로 알고리즘을 풀 때 BufferedReader를 이용한 해설을 많이 볼 수 있다. 많은 데이터를 입력 받아야 할 상황에서 사용하도록 하자.
BufferedReader 사용법
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in)); //선언
String s = bf.readLine(); //String
int i = Integer.parseInt(bf.readLine()); //Int
선언은 위의 예제와 같이 하면 된다.
입력은 readLine( ); 메소드를 활용하면 되는데 여기서 주의할 점이 두 가지가 있다.
- 첫 번째는 readLine( ) 메소드의 리턴 값은 String으로 고정되기에 String이 아닌 다른 타입으로 입력을 받으려면 형변환을 꼭 해주어야 한다.
- 두 번째는 예외 처리를 꼭 해주어야 한다. readLine( );을 할 때마다 try & catch를 활용하여 예외 처리를 해주어도 되지만 대게 throws IOException을 통하여 작업한다.
Ex1) try & catch 사용
import java.io.*;
public class BufferedReaderEx1 {
public static void main(String args[]) {
try{ //예외 처리 필수! 또는 throwsIOException 해주기
//콘솔에서 입력 받을 경우
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
//파일에서 입력 받을 경우
FileReader fr = new FileReader("BufferedReaderEx1.java");
BufferedReader br_f = new BufferedReader(fr);
//String이 리턴 값이라 형변환 필수! 라인 단위
int num = Integer.parseInt(br.readLine());
br.close(); //입출력이 끝난 후 닫아주기
//파일의 한 줄 한 줄 읽어서 출력하기
String line ="";
for(int index = 1; (line = br_f.readLine()) != null; index++){
System.out.println(line);
}
} catch (IOException e){
e.printStackTrace();
System.out.println(e.getMessage());
}
}
}
Ex2) throws IOException 사용
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class BufferedReaderEx2{
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); //선언
String str = br.readLine(); //라인 단위로 입력 받기
int i = Integer.parseInt(br.readLine()); //정수형 입력, 형변환 필요
}
}
Read한 데이터 가공
Read한 데이터는 Line 단위로만 나눠지기 때문에 Space(띄어쓰기) 단위로 데이터를 가공하려면 따로 작업을 해주어야 한다. 아래의 두 가지 방법이 대표적이다.
StringTokenizer st = new StringTokenizer(s); //StringTokenizer인자값에 입력 문자열 넣기
int a = Integer.parseInt(st.nextToken()); //첫번째 호출
int b = Integer.parseInt(st.nextToken()); //두번째 호출
String array[] = s.split(""); //Space(띄어쓰기, 공백)마다 데이터 끊어서 배열에 넣기
- 첫 번째 방법은 StringTokenizer에 nextToken( ) 함수를 쓰면 readLine( )을 통해 입력받은 값을 Space단위로 구분하여 순서대로 호출할 수 있다.
- 두 번째 방법은 String.split( ) 함수를 활용하여 배열에 Space단위로 끊어서 데이터를 넣고 사용하는 방식이다.
BufferedReader 클래스의 또 다른 메인 함수들
Modifier and Type | Method and Description |
void | close ( ) - 입력 스트림을 닫고 사용하던 자원들을 푼다. |
void | mark(int, readAheadLimit) - 스트림의 현재 위치를 마킹한다. |
boolean | markSupported( ) - 스트림이 mark기능을 지원하는지 true/false로 알려준다. |
int | read( ) - 한 글자만 읽어 정수형으로 반환해준다. 즉 '3'이라고 읽어서 '3'의 정수형인 (int)'3'=51을 리턴 |
int | read(char[ ] cbuf, int offset, int length) - cbuf의 offset위치 부터 length길이 만큼 문자를 스트림으로부터 읽어온다. |
String | readLine( ) - 스트림으로부터 한 줄을 읽어 String(문자열)으로 리턴 |
boolean | ready( ) - 입력 스트림이 사용할 준비가 되어있는지 확인해준다. |
void | reset( ) - 마킹이 있으면 그 위치부터 다시 시작, 그렇지 않으면 처음부터 다시 시작. |
long | skip(long n) - n개의 문자를 건너뛴다. |
BufferedWriter
일반적으로 출력을 할 때 System.out.println(" "); 방식을 사용했다. 적은 양의 출력일 경우 성능 차이가 미미하겠지만 많은 양의 출력에서는 입력과 마찬가지로 버퍼를 활용해주는 것이 좋다.
BufferedWriter 사용법
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); //선언
String s = "ro117youshin"; //출력할 문자열
bw.write(s); //출력
bw.newLine(); //줄바꿈
bw.flush(); // 남아 있는 데이터를 모두 출력
bw.close(); // 스트림을 닫음
BufferedWriter의 경우 버퍼를 잡아 놓았기 때문에 flush( ) / close( )를 반드시 호출해주어 뒤처리를 해주어야 한다. 그리고 bw.write에는 System.out.println( );과 같이 자동 개행 기능이 없기 때문에 개행을 해주어야 할 경우 \n을 통해 따로 처리해주어야 한다.
Ex1) throws IOException 사용
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.StringTokenizer;
//ctrl+shift+o
public class BufferedWriterEx1 {
public static void main(String[] args) throws IOExcepiton{
BufferedReader bf = new BufferedReader(InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(OutputStreamWriter(System.out));
StringTokenizer st;
int cnt = Integer.parseInt(bf.readLineA());
for (int index = 0; index < cnt; index++){
st = new StringTokenizer(bf.readLine());
bw.write((Integer.parseInt(st.nextToken()) + Integer.parseInt(st.nextToken())) + "\n");
}
bw.close();
}
}
BufferedWriter 클래스의 또 다른 메인 함수들
Modifier and Type | Method and Description |
void | close( ) - 스트림을 닫는다. 닫기 전 반드시 flushing 하기. |
void | flush( ) - 스트림을 비운다. |
void | newLine( ) - Enter 개행 역할을 한다. |
void | write(char[ ] cbuf, int offset, int length) - 버퍼 offset위치부터 length크기만큼 write 쓴다. |
void | write(int c) - 한 글자 쓰기 |
void | write(String s, int offset, int length) - 문자열에서 offset부터 일정 길이만큼 write 써준다. |
Reference
1. 양햄찌가 만드는 세상 - [Java 자바 입출력] BufferedReader/BufferedWriter
2. 코딩팩토리 - [Java] BufferedReader, BufferedWriter를 활용한 빠른 입출력
3. 크크의 개발 블로그 - [Java] 빠른 입출력을 위한 BufferedReader, BufferedWriter, StringTokenizer, StringBuilder
'JAVA' 카테고리의 다른 글
[Java] 📚 List에서 Array로 변환, Array에서 List로 변환 (2) | 2022.08.18 |
---|---|
[Java] 📚 배열 정렬하기 Arrays.sort() (0) | 2022.08.17 |
[Java] 📚 String / StringBuffer / StringBuilder (1) | 2022.05.22 |
[JAVA] Exception in thread "main" java.lang.StringIndexOutOfBoundsException (6) | 2022.03.12 |
[JAVA] Exception in thread "main" java.lang.ArithmeticException: / by zero (2) | 2022.03.09 |