blues_log

오늘 학습한 내용

  • Java : 예외 처리, 빠른 입출력(BufferedReader, BufferedWriter)

문제상황

오늘의 문제상황은 백준 프로그래밍 문제를 풀다가 발생했다

이제 조금 Scanner에 익숙해진 것 같다고 생각했는데.. 

Scanner는 입출력 방식이 느려서 많은 데이터를 입출력하는 경우에는 시간이 오래 걸릴 수 있다는 사실을 알았다..

 

그럴때는 위와 같이 BufferedReader, BufferedWriter를 사용하라고 하셨는데 이와 관련된 내용은 잘 모르기 때문에 이 부분에 대해서 공부를 하기로 했다.

 

 

문제링크는 다음과 같다.

https://www.acmicpc.net/problem/15552

 

15552번: 빠른 A+B

첫 줄에 테스트케이스의 개수 T가 주어진다. T는 최대 1,000,000이다. 다음 T줄에는 각각 두 정수 A와 B가 주어진다. A와 B는 1 이상, 1,000 이하이다.

www.acmicpc.net


시도한 내용

우선, Scanner를 이용해서 문제를 풀어보었다.

import java.util.*;

public class Main{
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int A = sc.nextInt();

        for (int i=0; i<A; i++) {
            int B = sc.nextInt();
            int C = sc.nextInt();
            System.out.println(B+C);
        }
    }
}

결과는 당연히..

실제로 buffer와 scanner가 얼마나 차이나는지 알기 위해서 검색을 해봤다.

https://www.acmicpc.net/blog/search/%EC%9E%85%EB%A0%A5+%EC%86%8D%EB%8F%84 

위의 링크에서 buffer는 평균 0.6585, scanner는 4.8448초가 걸렸다. 거의 7배나 차이가 나는 것이다.

 

일단, Scanner를 쓰면 왜 느리고, Buffer를 사용하면 왜 빠른지 이해가 필요하다고 생각했다.

Scanner와 Buffer의 속도가 차이나는 이유는 

  • Scanner는 1KB 크기의 버퍼를 갖기 때문에 바로 입력이 전달된다.
  • Scanner는 읽는 과정에서 내부에서 정규식, 입력값 분학, 파싱 과정 등을 거친다.
  • BufferedReader는 8KB 크기의 버퍼를 가져서 buffer에 입력들을 저장했다가 한 번에 전송한다.

 

내가 이해한 내용으로는 물건을 한 개씩 옮기는 것보다는 상자에 담아서 한 번에 옮기는 것이 훨씬 빠른 것과 같은 것이라 생각했다.

 

 

이제 속도의 차이가 나는 이유에 대해서 알았으니 다음은 Buffer를 사용하는 방법에 대해서 공부하는 것이었다.

 

BufferedReader

  • 입력 스트림에서 문자를 읽는 함수
  • 문자나 배열, 라인들을 효율적으로 읽기 위해 문자들을 버퍼에 저장하고 읽는 방법을 취한다.
  • readLine() 메소드를 이용하여 데이터를 줄 단위로 읽는다.
  • 개행(엔터)만 경계로 인식하고 리턴값이 String으로 고정이다. (즉, 다른 타입으로 입력을 받으려면 형변환 필수!)

BufferedWriter

  • 출력을 담당하는 함수
  • 자동 개행 기능이 없기 떄문에 개행이 필요한 경우 '\n'을 통해서 처리해야 한다.

Buffer를 사용할때

  • IOException의 예외처리가 필수적이다.
  • 사용자의 입력은 여러 다양한 타입으로 들어올 수 있는데, 잘못된 값이 들어올 경우 에러가 발생할 수 있기 때문이다.

 

위의 내용을 토대로 로직을 작성했다.

import java.io.*;

public class Main{
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

        int A = Integer.parseInt(br.readLine());

        for (int i=0; i<A; i++) {
            String[] arr_str = br.readLine().split(" ");
            int B = Integer.parseInt(arr_str[0]);
            int C = Integer.parseInt(arr_str[1]);
            bw.write(B+C);
            bw.newLine();
        }
        bw.flush();
    }
}

그런데 결과는 

??! 결과는 원하는 대로 나오지 않았다.

 

거의 1시간 가까운 시간동안 여러가지를 시도해본 결과 bw.newLine말고 개행문자 "\r\n"을 사용하면 원하던 결과가 출력되는 것을 알게되었다.

 

계속 찾아봐도 정확한 사실은 알 수 없었다..

 

앞으로 개행이 필요한 경우에는 그냥 "\r\n"을 사용해야겠다..ㅎㅎ.. 


해결

학습한 내용을 토대로 작성한 코드는 다음과 같다.

import java.io.*;

public class Main{
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

        int A = Integer.parseInt(br.readLine());

        for (int i=0; i<A; i++) {
            String[] arr_str = br.readLine().split(" ");
            int B = Integer.parseInt(arr_str[0]);
            int C = Integer.parseInt(arr_str[1]);
            bw.write(B+C+"\r\n");
        }
        bw.flush(); //bw.close() 해도 출력됨
    }
}

결과는.. 두근두근..

2차 ??!

찾아보니 정답과 거의 유사한데, 공백, 빈 줄과 같은 문제로 인해서 출력 결과가 일치하지 않은 경우라고 한다..

혹시 몰라서 개행문자를 "\n"을 사용하니

다행히 통과가 되었따.. 백준 제출할때는 개행문자를 "\n"을 사용하쟈..


알게된 내용

문제를 풀 때 입력 받을 테스트 케이스가 10만개 이상이면 buffer를, 아니면 Scanner를 사용하는 연습을 하자.

 

새로운 사실을 알게 된다는건 언제나 신기하고 즐거운 일인 것 같다.

프로그래밍 공부하면서 정말 이런 경험을 많이 하는 것 같아서 정말 좋은 것 같다.

더욱 많이 배우고 성장하자 !!

 


참고

https://doozi316.github.io/java/2021/03/22/JAVA1/

https://dlee0129.tistory.com/238