개발 일기

[Java] 자바 기본적인 입출력 방법 비교(Scanner와 BufferedReader 그리고 BufferedWriter) 본문

Back-End/Java

[Java] 자바 기본적인 입출력 방법 비교(Scanner와 BufferedReader 그리고 BufferedWriter)

개발 일기장 주인 2024. 10. 21. 17:40

코테에서 터미널에서 입력받는 로직을 처리해야하는 경우가 있다. 크게 Scanner 클래스와 BufferedReader 클래스를 통해 처리할 수 있을텐데 이 두개를 중심으로 정리해보고자 한다.


Scanner

  • Scanner란 사용자로 부터 입력을 받을 수 있도록 도와주는 것으로 콘솔창에서 입력이 필요할때 사용
  • java.util.Scanner 패키지에 포함
  • 상대적으로 BufferedReader에 비해서 사용하기 용이한 메서드들을 클래스에서 많이 제공
  • 데이터를 파싱하기 위해서 내부적으로 정규 표현식 등을 사용하여 BufferedReader에 비해 내부적으로 복잡한 과정을 거쳐 처리 시간이 더 오래 걸린다. "작은 접시를 가지고 계속 오가며 음식을 조금씩 담아요"라고 타 블로그에서 설명했다.
  • 입력량이 고정되어 있고, 그 양이 많지 않은 경우에는 Scanner를 사용하여 편리하게 입력을 받는 것을, 입력량이 정해져 있지 않거나, 많은 경우에는 Scanner보다 BufferedReader의 사용을 고려할 것
  • 토큰을 기준으로 데이터를 입력받는다.
    *토큰 : 공백 문자(Spacebar), 탭(Tab), 개행(Enter) 등으로 구분되는 요소

https://hstory0208.tistory.com/entry/Java%EC%9E%90%EB%B0%94-Scanner-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%9E%85%EB%A0%A5%EB%B0%9B%EA%B8%B0

 

nextLine()과 기타 next_()의 차이

class Example1 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("Enter a line:");
        String line = sc.nextLine();
        System.out.println("You entered: " + line);
    }
}
// 입력: Hello world! How are you?
// 출력: You entered: Hello world! How are you?

class Example2 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("Enter a word:");
        String word = sc.next();
        System.out.println("You entered: " + word);
    }
}
// 입력: Hello world!
// 출력: You entered: Hello

nextLine()은 공백(스페이스)과 탭 등 모든 문자를 포함해서 한 줄 전체를 읽으며 사용자가 엔터키를 눌러 줄바꿈이 발생할 때까지의 모든 입력을 처리한다. 그러나 next_()는 공백(스페이스, 탭, 줄바꿈)을 기준으로 하나의 단어를 입력받는다.

 

hasNext()

hasNext()는 Scanner가 입력 스트림에 다음으로 읽을 토큰이 있을 때 true를 반환한다. 만약 입력 스트림에 더 이상 토큰이 없으면 false를 반환한다. 즉, 입력되는 토큰의 갯수가 확실하지 않은 경우 사용하면 좋을 것 같다.

class Example3 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        do {
            System.out.println("a값을 입력해주세요.");
            int a = sc.nextInt();
 
            System.out.println("b값을 입력해주세요.");
            int b = sc.nextInt();
 
            System.out.println("두 값의 합: " + (a + b));
        } while (sc.hasNextInt());
    }
}

 

이때 Scanner.class는 위와 같이 다양한 메서드를 제공하면서 간단하게 입력값들을 처리할 수 있도록 도와주지만 토큰을 하나씩 직접 처리해줘야하해서 토큰의 갯수가 많아질 수록 효율이 떨어질 것 같고 성능도 더 안좋아지긴 할 것이다.
그래서 적절히 입력값이 간단하고 짧은 경우에 적합한 것 같다.

BufferedReader

  • Scanner에 비해 대량의 데이터를 빠르게 읽어오는 데 유리
  • "마치 큰 통에 음식을 가득 담아 한 번에 가져오는 것과 같아요. 예를 들어, 뷔페에서 접시를 가득 채워 한 번에 많이 가져오는 거예요. 이렇게 하면 여러 번 왔다 갔다 하지 않아도 되니까 훨씬 빠르게 음식을 먹을 수 있겠죠?" 타 블로그에서 들고온 말인데 가장 잘 설명한 것 같다.
  • 데이터를 한번에 버퍼(메모리)라는 저장 공간에 저장하고 그 데이터를 꺼내 쓰는 것이다.
https://www.acmicpc.net/blog/view/56 여기 입력 속도 비교해놓은 블로그가 있는데
Java Scanner는 평균 4.8448초
Java BufferedReader, Integer.parseInt 평균 0.6585초
로 확연하게 BufferdReader가 빠른 것을 확인할 수 있다.
입력 처리 속도

 

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int a = Integer.parseInt(br.readLine());
  • 주로 한줄 씩 입력 받기 위해 위와 같이 readLine() 메소드를 활용한다.
  • 그러나 이때 정수와 같은 특정 데이터 형식을 직접적으로 읽는 메서드를 제공하지 않아  데이터를 읽어온 후, 문자열을 형변환해서 사용해야한다.

StringTokenizer

문자열을 특정 구분자(기본적으로 공백)로 분리하여 빠르게 처리할 수 있게 해주는 클래스로 여러 개의 숫자나 문자열이 공백으로 구분된 입력을 처리할 때 유용하다. 입력된 문자열을 토큰(token) 단위로 나누어 각 토큰을 순차적으로 처리할 수 있다.

그래서 공백으로 구분된 여러 값을 입력받아야 하는 경우 BufferedReader와 StringTokenizer를 함께 사용하면 효율적이다.(BufferedReader로 한 줄의 입력을 읽고, StringTokenizer를 사용해 공백을 기준으로 값을 나눈 후, 필요한 형식으로 변환)

// Only BufferedReader
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] inputs = br.readLine().split(" ");

int a = Integer.parseInt(inputs[0]);
int b = Integer.parseInt(inputs[1]);
int c = Integer.parseInt(inputs[2]);

// StringTokenizer
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());

int a = Integer.parseInt(st.nextToken());
int b = Integer.parseInt(st.nextToken());
int c = Integer.parseInt(st.nextToken());

성능 면에서는 StringTokenizer가 약간 더 유리하다고 한다.


마지막으로 출력을 위한 클래스도 알아보겠다.

BufferedWriter

  • 대량의 데이터를 출력해야 할 때, System.out.println()보다 성능상 이점이 있어, 효율적인 출력이 필요할 때 사용
  • 주로 파일 쓰기나, 콘솔에 많은 양의 데이터를 출력할 때 사용
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
bw.write("hello bf!");
bw.newLine(); // 줄바꿈
bw.flush();
bw.close();
  • bw.write() 메서드로 인자 값을 버퍼에 저장
  • 파라미터로 char 값이나 String을 받을 수 있고 숫자를 출력하려면 String.valueOf()로 변환해야한다.
  • bw.flush()로 버퍼에 저장된 데이터를 비우고 해당 데이터를 출력하며 bw.close()로 스트림을 닫는다.

StringBuilder

  • 문자열을 효율적으로 조작하기 위해 제공되는 클래스
  • 내부 Buffer에 문자열 저장, buffer에서 추가, 수정, 삭제
    append(String str): 문자열을 끝에 추가합니다. 다양한 자료형(문자, 정수 등)을 추가
    insert(int offset, String str): 지정한 위치에 문자열을 삽입
    delete(int start, int end): 지정한 범위의 문자열을 삭제
    reverse(): 문자열의 순서를 뒤집기
    toString(): StringBuilder 객체를 String 객체로 변환
    subString(int start, int end): 지정한 범위의 문자열을 추출
public class Main {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();
        
        // 문자열 추가
        sb.append("Hello");
        sb.append(" ");
        sb.append("World!");
        
        // 특정 위치에 문자열 삽입
        sb.insert(5, ",");
        
        // 문자열을 뒤집기
        sb.reverse();
        
        // String으로 변환하여 출력
        System.out.println(sb.toString());
    }
}
  • StringBuilder가 BufferedWrtier보다성능이 더 좋은듯하다.