Java

[ JAVA ] BufferedReader , StringTokenizer, StringBuilder 을 사용하는 이유

kminnnee 2023. 8. 12. 15:19

BufferedReader, StringTokenizer 는 문자열로 활용하기 위하여 사용되며 Scanner를 사용하는 것보다 빠르다.

백준이나 인프런에 있는 알고리즘 문제를 풀면서 문자에 대한 입력을 처리할 때, 지금까지는 scanner 를 많이 사용했지만, 문제를 풀면서 시간초과 문제가 발생했고, scanner 대신 BufferedReader 를 사용하면 버퍼(Buffer)를 사용하여서 알고리즘 문제를 풀 때, 시간복잡도 효율성에서 유리하다는 사실을 알고 본격적으로 공부 시작 !

1 . 그렇다면 버퍼(Buffer) 는 무엇일까?

데이터를 한 곳에서 다른 한 곳으로 전송하는 동안 일시적으로 그 데이터를 보관하는 임시 메모리 영역
버퍼를 이용한 입력 : BufferedReader
버퍼를 이용한 출력 : BufferedWriter

https://jhnyang.tistory.com/entry/Java-자바-입출력-BufferedReaderBufferedWriter

2. BufferedReader 란 ?

JAVA에서 입력방식은 Scanner와 BufferedReader가 있다. Scanner를 통해 입력을 받을경우 Space Enter를 모두 경계로 인식하기에 입력받은 데이터를 가공하기 매우 편리하다.
하지만!! BufferedReader는 Enter만 경계로 인식하고 받은 데이터는 String으로 입력을 받기 때문에 가공을 해야하는 작업이 필요하다. 하지만 작업속도 차이가 많이 나기 때문에 BufferedReader를 이용하여 입력 받는 것이 훨씬 효율적이다.

처음에 공부하면서 나도 헷갈리는것들이 많았기 때문에,

scanner 예시

public class Main {
    public static void main(String[] args)  {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt(); 	// 2 입력
        int m = scanner.nextInt(); 	// 3 입력
        String str = scanner.next();  // hi 입력
        int[] A = new int[n];
        for(int i=0; i<n; i++) { 
            A[i] = scanner.nextInt();    //  1  2 입력
        }
        System.out.println(n);		// 2 출력
        System.out.println(m);     //  3 출력
        System.out.println(str); 	// hi 출력
        for(int num : A) {
            System.out.print(num + " "); 	// 1 2 출력
        }
    }
}

< 입력 >
2
3
hi
1 2
< 출력 >
2
3
hi
1 2 

BufferedReader 예시

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

readLine() 을 통해서 데이터를 라인단위로 읽을 수 있도록 해준다.
코드를 자세히 설명하자면

BufferedReader 객체를 생성한다.
BufferedReader는 입력 스트림에서 문자열을 읽어오기 위한 클래스. 

InputStreamReader(System.in)은 표준 입력(System.in)을 문자열로 변환하는 InputStreamReader 객체를 생성하는 부분.
따라서 BufferedReader를 사용하여 표준 입력으로부터 문자열을 읽어올 수 있다.

int N = Integer.parseInt(br.readLine()); BufferedReader의 readLine() 메소드를 사용하여 표준 입력에서 한 줄의 문자열을 읽어옵니다. 사용자가 엔터를 입력하기 전까지의 모든 문자열을 한 번에 읽어온다. 

그리고 Integer.parseInt()를 사용하여 읽어온 문자열을 정수로 변환하여 int 타입 변수 N에 저장한다. 

이렇게 하면 사용자가 입력한 숫자가 정수로 변환되어 N 변수에 저장되게 된다.
예를 들어, 사용자가 키보드로 숫자 "123"을 입력하고 엔터를 누르면, br.readLine()은 "123"이라는 문자열을 읽어온다. 

그리고 Integer.parseInt()를 사용하여 "123"을 정수로 변환하면 N 변수에는 숫자 123이 저장된다.

3. StringTokenizer

  • BufferedReader 로 라인을 읽고 그 라인에서 특정 문자열로 읽어드리기위해서는 StringTokenizer 클래스를 사용하여야 한다. ( throws IOException 추가 ) 
  • 라인 한줄을 읽고 다음 라인을 읽고 싶을 때는 new StringTokenizer(br.readLine()); 을 추가하여야 한다.
  • 또한 특정 문자열을 단위로 읽고 싶다면
    new StringTokenizer(st.nextToken())

예시 1

public class Baek_2559 {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int N = Integer.parseInt(st.nextToken());
        int M = Integer.parseInt(st.nextToken());
        int[] A = new int[N];

        st = new StringTokenizer(br.readLine());
    
        int K = Integer.parseInt(br.readLine());
        System.out.println(N + " " + M+ " " + K);
    }
}

<입력>
1 2
3
4
<출력>
1 2 4

처음에 이 코드에서 의문이 들었던 점은
1. 3을 입력하고 프로그램이 바로 출력이 안되는 점
2. int K = Integer.parseInt(br.readLine()); 을 통해서 3이 들어가 있을거라고 생각했는데 4가 들어가서 출력이 되는 점

이었다.
.

.

그래서 알아보니

int K = Integer.parseInt(br.readLine());
부분에서는 사용자가 입력한 문자열 중에서 마지막으로 입력한 값인 "4"만 읽어와서 K 변수에 저장되고,
"3"은 읽어오지 못하는 것이다.
따라서 출력 결과도 "1 2 4"가 나오게 된다.

예시 2

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int N = Integer.parseInt(st.nextToken());
        int M = Integer.parseInt(st.nextToken());
        int[] A = new int[N];

        st = new StringTokenizer(br.readLine());
        int K = Integer.parseInt(st.nextToken());
        System.out.println(N + " " + M+ " " + K); 

<입력>
1 2
3
<출력>
1 2 3

예시2의 코드는 st 객체를 다시 생성하여 새로운 입력을 처리한다. 처음에 StringTokenizer st = new StringTokenizer(br.readLine());를 사용하여 한 줄의 문자열을 읽어와서 st 객체에 저장한다.
그리고 st.nextToken()을 사용하여 첫 번째 토큰(단어)인 정수 N을 가져온다. 그 다음에 다시 st.nextToken()을 사용하여 두 번째 토큰(단어)인 정수 M을 가져오는 것이다. 그리고 이후에도 st.nextToken()을 사용하여 새로운 토큰을 가져올 수 있다.

4. split vs StringTokenizer

StringTokenizer는 공백이 있다면 뒤에 문자열이 공백 자리를 땡겨 채우도록 한다.

StringTokenizer가 BufferedReader보다 빠르게 사용될 수 있다.

문자열을 자르게 위해 split을 사용할땐, split은 정규식을 기반으로 자르는 로직으로서 내부는 복잡하다. 그에 비해 StringTokenizer의 nextToken()메소드는 단순히 공백 자리를 땡겨 채우는 것이다. 정규식 처리가 딱히 필요한게 아닌 경우 StringTokenizer가 효율적이다.

정규식이나 인덱스 접근과 같은 처리가 필요없다면 StringTokenizer를 사용하는 것이 효율적이다.


공부해야할 부분들이 너무 많다, 매일 꾸준히하자 !