JAVA

[Java] 입출력 BufferedReader / BufferedWriter

로춘남 2021. 10. 3. 10:26
728x90


알고리즘 공부하다가 Scanner를 사용하는일이 있었는데, 여러 예제를 찾아보니 BufferedReader와 BufferedWriter가 알고리즘 측면에서 더 효율이 높다는것을 발견했다. 초기 System.out.print할 데이터의 양이 적다면, 큰 차이는 안난다고하는데, 데이터의 양이 커질수록 Scanner와 BufferedReader/BufferedWriter의 차이는 더 발생한다고한다.

 

BufferedReader / BufferedWriter

이름에서도 알 수 있겠지만, 이것은 각각 Reader와 Writer. 입출력을 담당하고 있다. 버퍼를 통해서 입출력을 전달해준다고한다.

 

그렇다면 왜? BufferedReader / BufferedWriter가 Scanner보다 더 효율이 좋은 이유는 무엇일까?

결론부터 말하자면 그것은 바로 버퍼를 이용해서 그렇다고한다.

 

참고: https://jhnyang.tistory.com/92

버퍼는 우리가 키보드에 입력 할때마다 값을 전달하는것이 아니라 버퍼에 정해진 용량만큼 모았다가 전달을 해준다.

예를들어 택배배송을 할때, 물건 하나씩 하나씩 옮기는것보단 한꺼번에 옮기는것이 더 효율적이지 않겠는가?

 

출력도 마찬가지로 버퍼를 통해서 프로그램에 전달이 되기때문에 시스템의 데이터처리 효율성을 높여줄수밖에 없다. 버퍼를 사용 할때, InpuytStream과 OutputStream를 사용하는 이유가 입출력 스트림으로부터 미리 버퍼에 데이터를 담아주기 때문에 Scanner보다 효율적인 출력이 가능한것이다.

 

- 버퍼(buffer)
: 데이터를 한곳에서 다른 한 곳으로 전송하는 동안 일시적으로 그 데이터를 보관하는 임시 메모리 영역
: 입출력 속도 향상을 위해 버퍼를 사용
- 버퍼 플러시(buffer flush): 버퍼에 남아있는 데이터를 출력시킨다(버퍼를 비우는 동작)
- BufferedReader: 버퍼를 이용한 입력
- BufferedWriter: 버퍼를 이용한 출력

처음에는 마냥 Scanner보다 효율이 좋다고해서 몇 번 써봤는데, 버퍼의 개념을 알고나니깐 조금은 이해력이 더 올라간것을 느낄 수가 있었다.

 

BufferedReader

Scanner로 입력을 할 경우에는 SpaceEnter를 경계로 인식하여 입력한 데이터를 가공하기가 매우 쉽다. 하지만 BufferedReadr는 Enter만 경계로 인식하고 return 되는 데이터가 String으로 고정되기때문에 이를 int로 사용하기위해서는 약간의 변환을 해줘야한다.

 

[공식문서]
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. 
입력 스트림에서 문자를 읽는 함수인데 문자나 배열, 라인들을 효율적으로 읽기 위해서 문자들을 버퍼에 저장하고(버퍼링) 읽는 방법을 취한다고 한다. 버퍼 사이즈는 우리가 지정할 수도 있지만 지정안할 경우에는 기존 디폴트 사이즈가 사용된다고 나와 있다.  [출처] https://jhnyang.tistory.com/92

 

그렇다면 실제로 사용을 해볼까?

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
 
public class exam01{
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String str = br.readLine();
        int a = Integer.parseInt(str);
        
    }

해당코드가 BufferedReader의 가장 기본적인 예제라고 볼 수 있겠다. 입력하는 값이기때문에 BufferedReader를 설정해주는데 여기에 InputStreamReader로 같이해준다. 코드를 보면 br.readLin(); 이라는것이 있는데, 이것은 흔히 Scanner에서 말하는 nextLine(); 역할을 하는것이라 볼 수 있다. 또한 BufferedReader는 return 값이 String으로 고정되기때문에 int 타입으로 입력을 받기 위해선 Interger.parseInt(str)처럼 타입을 꼭 변환해줘야한다.

 

해당 코드는 값 1개를 입력 할 수 있는데, 만약 여러개의 코드를 입력해야한다면 어떻게 될까?

이럴때는 StringTokenizer 클래스를 이용하고, Splite를 통해 공백을 나눠줘서 작업을 해야한다.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class exam01{
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String str = br.readLine();   // 입력을 1 2 로 받음
        StringTokenizer st = new StringTokenizer(str);
        int a = Integer.parseInt(st.nextToken());  //1
        int b = Integer.parseInt(st.nextToken());  //2

        String[] array = str.split(" ");
        System.out.println(a);  //1
        System.out.println(b);  //2


        for (String k : array) {
            System.out.println(k);   // 1 2 출력됨
        }
    }
}

readLine() 메소드가 있기대문에 Scanner처럼 1과 2를 따로 따로 입력하는것이 아니라 1 2 처럼 한줄에 입력을 해줘야한다. 1 2로 입력을 했다면 " "로 된 공백을 포함하여 버퍼에 넣어주게되고, 이 공백을 나누기 위해서는 StringTokenizer와 Splite를 통해 입력한값을 나눠준다.

 

BufferedWriter

흔히 알고 있는 System.out.println();와 동일 기능을 하는 메소드다. 이것 또한 버퍼를 이용하기때문에 효율이 좋고 많이 양의 데이터를 처리할때 이득이다.

import java.io.*;
import java.util.StringTokenizer;

public class exam02{
    public static void main(String[] args) throws IOException {
        BufferedReader br= new BufferedReader(new InputStreamReader(System.in));
        String str = br.readLine();   // 입력을 5 4 로 받음
        StringTokenizer st = new StringTokenizer(str);
        int a = Integer.parseInt(st.nextToken());  //5
        int b = Integer.parseInt(st.nextToken());  //4

        String k = "나는로춘남";
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        bw.write(k + "\n");

        bw.flush();
        //bw.newLine();
        bw.write("5");

        bw.write(String.valueOf(b));

        bw.flush();
        bw.close();
    }
}

BufferedReader와 BufferedWriter를 사용한 예제이다. 해당 메소드를 사용할때는 bw.write(); 처럼 이것을 사용한다고해서 바로 출력이 되는것이 아니다. Scanner처럼 바로 적용되는것이 아니라 버퍼에 데이터를 저장했다가 버퍼가 flush나 close가 될때 한번에 Stream쪽으로 전달이 되기 때문이다. 따라서 해당값을 출력하기 위해선 bw.flush();와 bw.close();를 꼭 사용해줘야한다. 

만약 계속 사용하고 싶다면? bw.close();는 완전 기능을 닫아버리기 때문에 계속하기 위해선 bw.flush();를 이용해줘야한다. 줄바꿈이 필요하다면 newLine()을 이용해준다.

write()메소드는 String으로만 출력이 가능해서 정수값으로 출력하려면 String.valueOf()를 통해 타입을 변환해줘야한다.

 

이 메소드는 버퍼를 잡아두기때문에 flush()와 close()를 통하여 처리를 해줘야하며, System.out.println();처럼 자동적으로 개형처리가 안되기때문에 newLine()이나 \n을 통해 개행처리를 해주는것이 좋다.

메소드명 기능
BufferedReader(Reader rd) rd에 연결되는 문자입력 버퍼스트림 생성
BufferedWriter(Writer wt) wt에 연결되는 문자출력 버퍼스트림 생성
int read() 스트림으로부터 한 문자를 읽어서
int형으로 return
int read(char[] buf) 문자배열 buf의 크기만큼 문자를 읽어들임. 읽어들인 문자수를 return
int read(char[] buf, int offset, int length buf의 offset위치에서부터 length길이만큼 문자를 스트림으로부터 읽어들임
String readLine() 스트림으로부터 한 줄을 일기어 문자열로
return
void mark() 현재위치를 마킹, 차후 reset()을 이용하여
마킹위치부터 시작
void reset() 마킹이 있으면 그 위치에서부터 다시 시작,
그렇지 않으면 처음부터 다시 시작
long skip(int n) n개의 문자를 건너 뜀
void close() 스트림 닫음
void write(int c) int형으로 문자 데이터를
출력문자스트림으로 출력
void write(char[] buf, int offset, int length) 문자배열 buf의 offset 위치부터 length
길이만큼을 출력스트림으로 출력
void newLine() 줄바꿈 문자열 출력
void flush() 남아있는 데이터를 모두 출력

출처: https://coding-factory.tistory.com/251

728x90

'JAVA' 카테고리의 다른 글

[Spring] IoC, DI, 컨테이너  (0) 2021.10.19
[Java] Array 배열 Controller VO로 받기 파싱  (0) 2021.10.18
[Java] String을 Date 타입으로 변환 할 때  (0) 2021.08.19
[Java] Junit 실행순서  (0) 2021.07.28
[Java] Class에 대하여  (0) 2021.07.28