[Java] BufferedReader&BufferedWriter의 IOException 예외처리

Kwon·2024년 5월 22일
0

Java

목록 보기
15/15
post-thumbnail

BufferedReader & BufferedWriter?

몇 달 전 부터, 알고리즘을 풀며 Scanner 및 System.out.prinln() 보다 속도가 빠르다는 이유로 BufferedReader와 BufferedWriter을 사용해왔다. 하지만, 이 기능들을 사용하면 메모리와 시간만 절약하다는 것을 알 수 있었지만, 무슨 이유에서 사용하는 것인지 그리고 IOException 예외처리는 왜 하는 것인지 알고 싶었다.

public static void main(String[] args) throws IOException {
        BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringTokenizer st = new StringTokenizer(bf.readLine());
        int n = Integer.parseInt(st.nextToken());
        int m = Integer.parseInt(st.nextToken());
        int[] num = new int[m];
        st = new StringTokenizer(bf.readLine());
        for (int i = 0; i < m; i++) num[i] = Integer.parseInt(st.nextToken());
        bw.write(solution(n, m, num) + " ");
        bw.flush(); bw.close(); bf.close();
    }

1. BufferedReader & BufferedWriter

Scanner는 입력문자를 space, enter로 구분하고 BufferedReader는 enter로만 구분하고 버퍼 공간에 저장해 두었다가 한 번에 내보내는 방식이다. BufferedReader & BufferedWriter는 버퍼링을 통해 데이터를 읽고 쓰기 때문에 속도가 빠른 것을 알 수 있었는데, 버퍼링은 데이터를 작은 덩어리로 여러 번 읽고 쓰는 대신, 한 번에 큰 덩어리로 읽고 쓰는 방식이라고 한다.

그리고, Scanner는 휘발성이지만, BufferedReader는 String으로 가공하여 내보내는 작업이 필요하다고 한다. Scanner는 버퍼 사이즈가 1024 char 인 반면, BufferedReader는 8192 char 로 입력 값을 많이 받을 수 있다고 한다. 또한, 동기화가 가능하여 멀티 쓰레드 환경에선 유리한 기능이라고 볼 수 있다.

라인 단위 처리(Line-by-line processing)에서도 차이를 볼 수 있었는데, Scanner와 System.out.println() 는 라인 단위로 입,출력을 실행하지만, parsing 작업(오버헤드의 위험) 및 새로운 라인으로 이동하는 등 작업을 수행하는 반면, BufferedReader와 BufferedWriter는 라인 단위로 입력을 읽어오고 데이터를 버퍼에 모아 한 번에 출력하기 때문에 효율적이라는 결과를 볼 수 있었다.

👉 정리하자면,

  • 버퍼링(Buffering)
  • 입출력 스트림(Input/Output Stream)
  • 라인 단위 처리 (Line-by-line processing)

2. IOException 예외처리

보통, IOException을 쓰지 않을 때 오류나는 부분은 bf.readLine() 을 해주었을 때 빨간 줄이 발생한다. 인터넷에 검색해보고 readLine()메서드를 뜯어봤는데,

String readLine(boolean ignoreLF) throws IOException {
        StringBuilder s = null;
        int startChar;

        synchronized (lock) {
            ensureOpen();
            boolean omitLF = ignoreLF || skipLF;

        bufferLoop:
            for (;;) {

                if (nextChar >= nChars)
                    fill();
                if (nextChar >= nChars) { /* EOF */
                    if (s != null && s.length() > 0)
                        return s.toString();
                    else
                        return null;
                }
                boolean eol = false;
                char c = 0;
                int i;

                /* Skip a leftover '\n', if necessary */
                if (omitLF && (cb[nextChar] == '\n'))
                    nextChar++;
                skipLF = false;
                omitLF = false;

            charLoop:
                for (i = nextChar; i < nChars; i++) {
                    c = cb[i];
                    if ((c == '\n') || (c == '\r')) {
                        eol = true;
                        break charLoop;
                    }
                }

                startChar = nextChar;
                nextChar = i;

                if (eol) {
                    String str;
                    if (s == null) {
                        str = new String(cb, startChar, i - startChar);
                    } else {
                        s.append(cb, startChar, i - startChar);
                        str = s.toString();
                    }
                    nextChar++;
                    if (c == '\r') {
                        skipLF = true;
                    }
                    return str;
                }

                if (s == null)
                    s = new StringBuilder(defaultExpectedLineLength);
                s.append(cb, startChar, i - startChar);
            }
        }
    }

함수 내에서 synchronized 함수를 호출하는 것을 볼 수 있다.

synchronized?
synchronization01 - 링크텍스트
synchronization02 - 링크텍스트

한 마디로 처음에 설명했던 것 처럼, 동기화를 위해 동시 호출을 막아주는 메서드이며 다른 코드블럭은 대기 후 먼저 실행된 코드가 다 호출된 이후에 들어가서 실행된다고 한다. BufferedReaderd에서는 readLine에 접근하여 InputStreamReader가 열렸는지 확인해준다고 한다.

3. ensureOpen()

private void ensureOpen() throws IOException {
        if (in == null)
            throw new IOException("Stream closed");
    }

ensureOpen 함수에서는 in이 null일 경우 IOException을 던진다. 즉, readLine()에 입력된 값이 null일 때 대비하여 예외처리를 해준다고 한다. null인 경우, 변수를 선언할 때 int a = null 선언한 이후 a 라는 변수에 다른 입력 값이 들어가지 않은 상태로 null 그대로 전달 된 경우 IOException을 반환한다고 한다.

profile
📲 @bu_kwon_2 / 💻 dnu05043.log / ⌨ Back-end / 🦁 LikeLion

0개의 댓글