Scanner와 BufferedReader는 입력을 읽는 데 사용되는 클래스이다. 실제로 두 클래스 모두 내부적으로 버퍼링을 사용하여 데이터를 처리한다. 그러나 둘 사이에는 명확한 차이점이 존재하는데, 바로 입력 방식과 성능이다. 핵심은 이러한 차이점으로 인하여 각각의 사용 용도가 어느 정도 정해져 있다는 것인데, 본 포스팅에서는 두 클래스 간의 차이점을 비교 분석해보려고 한다.
주요 특징 비교
[Scanner]
Scanner는 다양한 자료형을 쉽게 처리하는 클래스이다. 이에 따라 다양한 메서드가 존재하는데, 예를 들어 nextLine()은 한 줄을 읽고, nextInt(), nextDouble()은 각각 정수와 실수 형식으로 입력을 받는다.
이렇듯 Scanner는 굉장히 좋아 보이지만 치명적인 단점이 있다. 편의성을 높이다 보니, 내부적으로 구분자를 사용하여 입력을 파싱 하는 과정이 필요하다. 즉, 성능 측면에서 상대적으로 느리다는 이야기다.
* 주의사항
사용자는 입력을 마친 후 Enter 키를 누를 것이다. 그러나 nextInt()와 nextDouble()의 경우, Enter 키로 인해 생긴 개행문자를 처리하지 않기 때문에 해당 개행문자는 여전히 입력 버퍼에 남아있게 된다. 이후 nextLine()을 사용한다면 버퍼에 남아있던 개행문자를 읽어 빈 문자열을 바로 반환하게 되는데, 이를 예방하기 위해 사전에 nextLine()을 호출하여 개행문자를 버리는 과정이 필요할 수 있다.
[사용처]
- 간단한 사용자 입력을 처리할 때
- 여러 자료형을 쉽게 처리할 때
- 공백을 포함한 데이터를 다룰 때
[BufferedReader]
반면, BufferedReader는 문자 단위로 입력을 처리하는 클래스이다. 기본적으로 readLine()을 사용해 한 줄씩 읽는다. 이 메서드는 사용자가 입력한 전체 줄을 문자열로 반환한다. 만약 숫자나 다른 자료형을 읽고 싶거든, 일단 문자열로 읽고 난 후에 수동으로 형 변환을 해야 한다.
자칫 BufferedReader가 불편하게 느껴질 수 있지만, 성능 측면에서는 훨씬 뛰어난 성능을 발휘한다. 이유는 버퍼링 된 입력 방식에서 찾아볼 수 있다. 이 입력 방식은 데이터를 한 번에 읽고 내부 버퍼에 저장하여 더 효율적으로 처리하게 돕는다.
* 참고사항
BufferedReader가 개행문자를 읽지조차 않는 것은 아니다. 당연히 Enter 키 입력에 따른 개행문자를 읽기는 하되, 단지 반환된 문자열에 포함시키지 않을 뿐이다.
[사용처]
- 성능 최적화가 중요할 때
- 많은 양의 텍스트 파일을 읽을 때
- 대규모 입력을 효율적으로 처리할 때
Q. '구분자'와 '파싱'이 무엇인가요?
* 구분자 (delimiter)
구분자는 입력 값이 어디에서 끝나고, 어디에서 새로운 값이 시작되는지를 결정하는 기준 문자이다. Scanner는 기본적으로 공백을 구분자로 사용한다. 예를 들어, "nice funczun"이라는 문자열을 입력받으면, "nice"와 "funczun"으로 분리해 각각 다른 데이터로 처리한다.
* 파싱 (parsing)
파싱은 입력된 데이터를 특정 규칙에 따라 분해하고 해석하는 과정이다. 예를 들어 Scanner는 사용자가 입력한 "1004"을 nextInt()로 읽으면, "1004"이라는 문자열을 정수 1004로 파싱 한다.
예제 코드 비교
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
// Scanner
Scanner scanner = new Scanner(System.in);
// 문자열 입력 받기
System.out.print("Enter your name: ");
String name = scanner.nextLine();
// 정수 입력 받기
System.out.print("Enter your age: ");
int age = scanner.nextInt();
// Scanner
scanner.close();
}
}
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
// BufferedReader
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// 문자열 입력 받기
System.out.print("Enter your name: ");
String name = br.readLine();
// 정수 입력 받기
System.out.print("Enter your age: ");
int age = Integer.parseInt(br.readLine()); // 문자열을 정수로 형 변환
// BufferedReader
br.close();
}
}
예제 코드를 두고 비교할 때, 둘 사이 차이점은 아래와 같다.
첫째, import 문이 다르다!
// Scanner
import java.util.Scanner
// BufferedReader
import java.io.*
이름부터 냄새가 난다.. 무슨 냄새?
- util = utility
- io = input/output
java.util 패키지는 유틸리티 클래스를 포함한다. (Ex. 컬렉션, 날짜/시간 등)
java.io 패키지는 입출력 클래스를 포함한다. (Ex. 네트워크 입출력, 파일 읽기/쓰기, 스트림 등)
둘째, 예외 처리와 형 변환 유무가 다르다!
// BufferedReader 예외 처리
throws IOException
// BufferedReader 형 변환 예시
Integer.parseInt()
별 거 아닌 것 같이 생긴 두 줄의 코드가 사람을 미치게 한다. 일단 쉬운 것부터 차근차근 가보자.
일단, 'BufferedReader 형 변환 예시'로 주석을 단 부분에서는 단순히 문자열을 정수로 변환하는 메서드라고 이해하자. 이 메서드는 주어진 문자열이 정수 형태로 유효한지 확인하고, 유효한 경우 그 값을 정수로 변환한다. (만약 주어진 문자열이 정수로 변환할 수 없는 형식이라면, NumberFormatException 예외를 던진다.)
여기서 문제는 '예외를 던지다'라는 표현이 무슨 의미인지 모르겠고, 'throws IOException' 역시 무슨 뜻인지 모르겠다는 것이다. 이것을 이해하려면 예외 처리가 무엇인지부터 알아야 한다.
예외 처리가 무엇인지, 그것을 던진다는 것은 어떤 의미인지, 예외 처리를 안 하면 어떻게 되는지, 예외 처리는 어떻게 하는 건지 등, 관련 내용은 다음 포스팅에서 알아보자.
'프로그래밍 > 배웠어' 카테고리의 다른 글
[IntelliJ] 인텔리제이 단축키 모음 (0) | 2025.01.10 |
---|---|
[Java] '예외 처리' 완전 정복하기 (0) | 2025.01.07 |
[Java] if 문, switch 문, 삼항 연산자 (0) | 2025.01.03 |
[Java] 객체의 실제 메모리 주소 얻기 (0) | 2024.12.30 |
[Java] ==, equals() /문자열 비교 정복하기 (1) | 2024.12.27 |