[백준] BuffereredReader 15552 문제 해결

2024. 1. 2. 17:04알고리즘

문제 상황

입력

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

출력

각 테스트케이스마다 A+B를 한 줄에 하나씩 순서대로 출력한다.

 

항상 Scanner만 사용했었는데, 백준에서 BufferedReader를 이용해 문제를 풀라고 요청했다.

 

BufferedReader란 무엇인가?

BufferedReader란 Buffer에 있는 IO 클래스이다. 

Scanner처럼 String, Int를 구별해주지도 못하고, Enter 키만 구분문자로 사용되지만,

Scanner에 비해서 속도가 빠르고, 많은 양을 저장할 수 있는 장점이 있다.

 

단, 사용 시에 반드시 try/catch문을 사용해 IO Exception을 잡아주어야 한다.

import java.io.*;

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

선언은 위의 코드 블럭처럼 할 수 있으며, io 패키지 내부에 속해있으므로, 반드시 io 패키지를 import 해주어야 한다.

또한 int와 String을 자동으로 구별하지 못하므로, parseInt를 통한 형변환이 필수적이다.

 

BufferedWriter란 무엇인가?

BufferedReader로 읽는다면, 쓰는 것(출력)도 필요하다.

이때 필요한 것이 BufferedWriter이다.

import java.io.*;

public class Main {
	public static void main(String[] args) throws IOException {
    	BufferedWriter bw = new BufferedWriter(new OutputStreamReader(System.in));
        String s = "abcdefg"; // 출력할 문자열
        bw.write(s); // 출력
        bw.newLine(); // 줄바꿈
        bw.flush(); // 남아있는 데이터를 모두 출력시킴
        bw.close(); // 스트림을 닫음
        
    }
}

 

BufferedWriter는 BufferedReader와 반대로, OutputStreamReader를 생성해야 한다.

주요 메서드

write(String data) 출력
newLine() 줄바꿈
flush() 남아있는 데이터 출력
close() 스트림을 닫음

BufferedWriter 관련 메서드는 위의 표와 같다. 

 

StringTokenizer란 무엇인가?

BufferedReader로 읽을 때 Enter로만 구별할 수 있다고 했다.

그렇다면 Enter가 아닌 문자열은 어떻게 구별할 수 있을까?

 

이때 사용하는 것이 바로 StringTokenizer이다. 

StringTokenizer은 java.util 패키지에 포함된 클래스로, 문자열을 토큰으로 분리하는데 사용된다.

사용 방식

import java.util.*;

public class Main {
	public static void main(String[] args) throws IOException {
    	StringTokenizer st = new StringTokenizer("this is a test"); // 첫 번째 방식
        StringTokenizer st = new StringTokenizer("this,is,a,test", ",") // 두 번째 방식
        
    }
}

 

사용은 두 가지 방식으로 진행할 수 있다.

  1. 단일 문자열을 인수로 받아 기본 구분자를 사용하는 것 => new StringTokenizer("this is a test")처럼 기본 구분자(공백, 탭, 개행문자, 캐리지리턴)을 이용해 자동으로 구별해준다.
  2. 사용자가 구분자를 명시적으로 지정하는 것 => new StringTokenizer("문자열", "명시적으로 지정한 구분자")를 활용해 수동(명시적)으로 구별해준다.

사용 예제

import java.util.*;

public class Main {
	public static void main(String[] args) throws IOException {
    	StringTokenizer st = new StringTokenizer("this is a test");
        while(st.hasMoreTokens()){
        	System.out.println(st.nextToken());
        }
        
    }
}

 

사용 예제는 위의 코드와 같다.

우선, StringTokenizer 생성자를 호출한 후,  토큰이 남아있다면(st.hasMoreTokens()가 true라면) 다음 토큰을 반환한다

토큰이 남아있지 않다면(st.hasMoreTokends()가 false라면) 출력을 종료한다. . 

위 예제의 결과는 아래와 같다.

더보기

this

is

a

test

주요 메서드

countTokens() 남아 있는 토큰의 수를 반환
hasMoreTokens() 더 이상 토큰이 있는지 여부를 반환
nextToken() 다음 토큰을 반환

 

StringTokenizer 관련 주요 메서드는 위의 표와 같다.

 

split 메서드란 무엇인가?

아마 StringTokenizer만 사용했을 때는 여전히 시간 초과 문제가 발생했을 것이다.

이때 사용해야하는 것이 바로 "split 메서드"이다.

split 메서드는 String 클래스의 메서드로, 주어진 정규 표현식을 사용하여 문자열을 부분 문자열로 분할하는데 사용된다.

이 메서드는 분할된 부분 문자열들을 포함하는 String 배열을 반환한다는 특징이 있다.

주어진 문자열.split("구분문자")로 문자열을 분할할 수 있다.

 

사용 예제

public class Main {
	public static void main(String[] args) {
    	String str = "apple, banana, orange";
        String[] fruits = str.split(", ");
        
        for(String fruit : fruit) {
        	System.out.println(fruit);
        }
        
    }
}

 

이처럼 split 메서드는  주어진 문자열을 분할하고, 분할된 문자열을 다시 새로운 문자열에 저장할 수 있다는 특징이 있다.

위의 예시처럼 BufferedReader에서도 split을 활용해 문자열을 분할할 수 있다.

 

split 메서드를 활용한 BufferedReader 문자열 분할

import java.io.*;

public class Main {
	public static void main(String[] args) throws IOException {
    	BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        for(int i = 0; i < 10 ; i++) {
        	String[] parts = br.readLine().split(" ");
            // int b = Integer.parseInt(parts[0]);
        }
        
    }
}

 

위의 예제는 BufferedReader로 입력을 받고, 공백을 구분자로 분할하여, parts 문자열에 넣는 방식이다.

추가적으로, Integer.parseInt를 활용해 parts 문자열에 있는 문자를 정수형으로 변환할 수도 있다.

 

StringBuilder란 무엇인가?

StringBuilder란 Java에서 문자열을 효율적으로 조작할 수 있도록 해주는 클래스이다.

String 클래스와 달리 문자열을 변경하거나 추가할 때 새로운 객체를 생성하지 않는다.

따라서 문자열 조작 작업이 많은 경우 성능을 크게 향상시킨다.

 

사용 예제

public class Main {
	public static void main(String[] args) {
    	StringBuilder sb = new StringBuilder();
        sb.append("Hello");
        sb.append(" ");
        sb.append("World");
        
        String result = sb.toString();
        System.out.println(result);
    }
}

 

위의 예제는 StringBuilder를 활용한 문자열 출력이다.

StringBuilder의 append를 통해 새로운 문자열을 추가할 수 있고, toString을 통해 표준 상태의 String 객체를 생성하여 반환한다. 

이 메서드는 StringBuilder의 내용을 String 형태로 출력하거나, 다른 문자열 연산을 수행하기 위해서도 필요하다.

 

결과

import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();
        int a = Integer.parseInt(br.readLine());

        for (int i = 0; i < a; i++) {
            String[] parts = br.readLine().split(" ");
            int b = Integer.parseInt(parts[0]);
            int c = Integer.parseInt(parts[1]);
            sb.append(b + c).append('\n');
        }

        System.out.print(sb.toString());
    }
}

 

모든 개념을 활용한 결과는 위의 코드와 같다.

  1. 사용자로부터 입력받은 데이터를 읽을 BufferedReader 객체를 생성한다.
  2. 새로운 객체 생성 없이 String 객체를 생성할 StringBuilder 객체를 생성한다.
  3. 사용자로부터 테스트 케이스의 수를 입력받는다. 이때 BufferedReader의 메서드를 활용하는데, BufferedReader 메서드의 경우 String만 구별 가능하므로 int로 형변환을 해준다.
  4. 테스트 케이스의 개수만큼 입력받을 반복문을 설정한다.
  5. String형의 parts 배열에 공백을 구분자로 A와 B를 구별하여 저장한다. 이때, A는 첫 번째로 입력되고, B는 두 번째로 입력되므로, parts[0]은 A, parts[1]은 B가 된다.
  6. 입력받은 정보를 int로 형변환 해준다. 다시 한 번 말하자면, BufferedReader 메서드는 String만 구별 가능하므로 int로 형 변환해주어야 한다.
  7. stringBuilder 메서드를 활용해 A+B의 값(b+c)와 '\n'을 연결해 String 객체로 저장한다.
  8. 저장된 문자열들(A+B+'\n')을 toString() 메서드를 활용해 한꺼번에 출력해준다.

이처럼 일차적으로 BufferReader 객체를 활용해 Scanner보다 빠르게 정보를 입력받고,

이차적으로 split 메서드를 활용해 StringTokenizer보다 빠르게 문자열을 분할하며,

삼차적으로 StringBuilder 객체의 메서드를 활용해 String보다 빠르게 문자열을 합치고, 출력할 수 있다. 

 

참고 자료

1. [Java] BufferReader, BufferedWriter를 활용한 빠른 입출력, 코딩팩토리, 2018.9.27

[Java] BufferedReader, BufferedWriter를 활용한 빠른 입출력

 

[Java] BufferedReader, BufferedWriter를 활용한 빠른 입출력

BufferedReader/BufferedWriter는 Buffer에 있는 IO 클래스입니다. 입력된 데이터가 바로 전달되지 않고 중간에 버퍼링이 된 후에 전달되됩니다. 출력도 마찬가지로 버퍼를 거쳐서 간접적으로 출력장치로

coding-factory.tistory.com

 

2. Chat gpt 4