[Java] BufferedReader vs scanner

2025. 9. 8. 20:36·Language/Java

도입

본격적으로 알고리즘 문풀에 들어간 김마루..

scanner로 쓰다가 무수한 시간초과 실패를 겪어버렸다.

이유가 뭔지 찾아보니 scanner는 내부적으로 입력한 데이터를 파싱하는게 복잡해서

BufferReader에 비해 시간이 더 오래 걸리는 것이였다.!!

 

 

그런 김에 둘의 사용법 비교도 하고 앞으로 BufferReader 사용법을

익숙해지기 위해 ㄱㄱ혓!

 

 

 

scanner

scanner는 자바에서 기본적으로 제공하는 클래스이다.

scanner도 기본적으로 버퍼를 사용하는데 사용하는 메소드에 따라서 다양하게 존재한다.

nextInt() / next() / nextline() 등등..

nextInt()의 경우 공백문자를 구분자로 사용하여 정수 형태의 토큰으로 구별한다.

즉, 구분자는 숫자를 구별해주는 역할이고 실제로는 버퍼에서 숫자 토큰만을 읽는다.

입력이 10 [space] 20 엔터[\n] 이렇게 들어왔다고 하면

10, 20은 숫자 토큰이고, [space], [\n]는 구분자로 구별된다.

nextInt()는 엔터키를 사용자가 치기 전까지 입력되는 모든 것을 버퍼에 저장한다.

엔터키를 사용자가 눌렀을 때 엔터키를 포함한 버퍼안에서 숫자 토큰 탐색을 시작한다.

현재 10이라는 숫자 토큰이 발견되었고 10을 읽고 버퍼에서 10을 반환하고 nextInt()는 종료된다.

int a = nextInt(); // a는 10

 

**공백문자는 space, tap, \n등등으로 구성된다**

 

 

 

 

10이 빠져나가고 현재 버퍼의 상황은 _ 20 \n이다.

만약 이 상태에서 다시한번 nextInt()를 사용하면 공백문자는 무시하고

다음 숫자인 20을 읽어서 버퍼에서 프로그램으로 전송한다.

int b = nextInt(); // b는 20

(원래는 nextInt()를 사용하기 때문에 버퍼에 \n가 계속 쌓이지만 편의상 그림에 표기하지 않았다..)

 

 

 

 

 

최종적으로 버퍼에는 공백문자(스페이즈, 엔터)만 존재하게 되고

아무런 문제가 없어 보이지만 scanner가 제공하는 다른 메소드를 사용할 때 문제가 발생한다.

 

 

 

 

 

아 참고로 nextInt()를 쓰는데 버퍼에서 문자가 발견되면 InputMismatchException를 발생시킨다.

 

 

 

 

 

 

 

다음은 next()에 대해 알아보자

next()는 String타입으로 반환을 하며 공백문자를 구분자로 사용하여 토큰을 구별한다.

 

 

 

 

현재 버퍼에는 기존에 있던 스페이스와 엔터에서 추가적으로 H i [space] K enter가 입력되었다.

 

 

 

 

 

 

 

 

여기서 next()를 사용하게 되면 입력되는 모든 것이 enter키를 만나기 전까지 저장된다.

enter키가 입력되면 버퍼에서 공백 문자를 제외하고 문자 토큰을 탐색 하기 시작한다,

지금은 H 문자가 가장 먼저 발견되었으며 H문자를 프로그램에 반환하고 next()메소드는 종료된다.

(여기도 next()를 여러번 쓰면 버퍼에  \n이 쌓이지만 편의상 표기하지 않았다.)

 

 

 

 

 

 

 

이렇게 계속 next()를 사용하게 되면 결과적으로 space \n space \n가 버퍼에 남게된다.

이유는 next()또한 문자만 반환하고 공백문자는 버퍼에 방치하기 때문이다.

 

 

 

 

 

 

자 이제 이 상태에서 nextline() 메소드를 사용해보자

nextline()은 공백문자을 구분자고 사용하지 않고 오직 개행문자(\n)만을 기준으로 토큰을 구별한다. 반환타입은 String이다.

여기서 추가적으로 앞의 nextInt(), next()는 토큰만 반환하고 공백문자는 버퍼에 그대로 방치했지만

nextline()의 경우에는 토큰을 반환하고 \n를 소비하여 버퍼에서 삭제시킨다.

 

 

 

 

현재 K와 엔터를 입력한 상태다.

 

 

 

 

 

이 상태에서 nextline()은 엔터를 입력받았기에 메소드가 종료되기 전 버퍼에서 \n문자를 찾는다.

 다만, 앞전에 버퍼에 남겨져 있던 \n이 먼저 발견되어 빈 문자열을 반환하면서 \n를 버퍼에서 삭제시킨다.

String a = nextline() // a는 "" 빈 문자열임

사용자는 "K" 를 받기 위해서는 2번의 nextline()을 사용해야 된다.

 

 

 

 

 

int n = sc.nextInt();
sc.nextLine();          // 남은 '\n' 폐기
String line = sc.nextLine();   // 여기서 진짜 줄 읽기

이처럼 nextInt()나 next() 다음 nextline()을 사용할 경우 빈 문자열이 반환되는 경우가 종종 생긴다.

그렇기 때문에 위 코드처럼 공백문자를 삭제하고 사용해야 되는 불편함이 존재한다.

 

 

 

이러한 불편함과 시간초과(성능이슈)로 인해 BufferdReader가 더 좋은 선택지로 여거진다.

다만 장점으로 scanner는 타입에 따라 유연하게 뽑아 낼 수 있는 장점이 있다.

 

 

 

 

 

 

 

 

 

 

BufferedReader

Scanner를 통해 입력을 받을 경우 space, enter tap을 모두 구분자로 인식하기 때문에 데이터를 가공하기에는 매우 편리하다.

다만 Scanner는 내부구조로 인해 작업속도 차이가 많이 나기 때문에 BufferedReader를 통해 입력 받는 것이 효율적이다.

특히 코테!

 

 

 

BufferedReader는 토큰 단위로 읽는게 아닌 readLine()를 통해 한 번에 한 줄 전체를 읽어 시스템 I/O 횟수를

줄여서 성능이 뛰어나며 readLine() 하나의 메소드로만 데이터를 읽기 때문에 scanner에서 여러 메소드간의

입력 버퍼 충돌 문제가 발생하지 않는다.

 

 

 

 

 

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

일단 버퍼리더는 바로 System.in에 연결할 수 없고 InputStreamReader를 통해 입력 스트림과 연결해야 된다.

또한 버퍼리더의 경우 IOException 예외 처리가 필수적이기 때문에 메소드 뒤에 throws나 try-catch문을 사용해야 된다.

 

 

 

 

 

 

데이터는 readLine()통해 읽을 수 있으며 Enter를 발견하기 전까지 모든 것을 버퍼에 저장한다.

현재 H _ 1 enter가 입력스트림을 통해 버퍼에 입력 되었다.

 

 

 

 

 

 

 

String line = br.readLine(); // line = "H  1"

 

readLine()의 경우 \n 구분자를 만날 경우 구분자를 제외한 모든 것을 버퍼에서 반환시키며 구분자를 버퍼에서 삭제시킨다.

사용자는 "H _ 1"이라는 문자열을 얻게 되고 이제 H, _, 1를 각각의 토큰으로 분리하는 작업이 필요하다.

대표적으로 Tokenizer와 split이 있으며 보통 Tokenizer를 주로 사용한다.

각각 장단점은 있지만 split의 경우 정규표현식을 통하여 다채롭게 사용가능 하다는 장점이 있고

문자열이 단순하게 구성될 경우 Tokenizer를 통하여 간편하게 분리가 가능한 장점이 있다.

 

 

 

 

 

 

 

 

 

Tokenizer

BufferedReader로 읽은 라인에서 토큰을 분리하고 싶을 때 사용되는 클래스로

공백을 기준으로 해당 문자열을 토큰으로 분리해준다.

 

 

 

StringTokenizer st = new StringTokenizer(line);
String a = st.nextToken(); // "H" 
int num2 = Integer.parseInt(st.nextToken()); // "1" -> 1

 

Tokenizer는 nextToken() 메소드를 통해서 토큰을 분리할 수 있는데 이때 공백을 기준으로 구분한다.

실제로 2개의 토큰으로 분리가 되고 이때 만약 숫자타입으로 변환이 필요하다면 Integer.parseInt를 통해 변환을 해주면 된다.

 

 

 

 

split

split의 경우에는 구분자를 직접 사용자가 지정해줘야 한다.

현재는 " "가 구분자로 사용되었다.

String[] tokens = line.split(" "); // tokens = {"10", "20", "30"}
int num1 = Integer.parseInt(tokens[0]); // 배열의 첫 번째 원소
int num2 = Integer.parseInt(tokens[1]); // 배열의 두 번째 원소
int num3 = Integer.parseInt(tokens[2]); // 배열의 세 번째 원소

즉 버퍼리더로 받아온 문자열이 간단하다면 Tokenizer를 사용하고

복잡한 문자열을 분리하고 싶을 때 정규 표현식을 사용하여 문자열을 분리한다.

 

 

 

 

 

이렇게 Scanner와 BufferedReader에 대해서 알아보았고

다음에는 출력인 System.out.println과 BufferedWriter에 대해서 알아보도록 하자(아마도..)

 

 

'Language > Java' 카테고리의 다른 글

[Java] Garbage Collector  (0) 2025.10.21
[Java] ArrayList를 인터페이스로 업캐스팅 하는 이유  (0) 2025.08.27
'Language/Java' 카테고리의 다른 글
  • [Java] Garbage Collector
  • [Java] ArrayList를 인터페이스로 업캐스팅 하는 이유
김마루
김마루
개발자 취직 원해요
  • 김마루
    허거덩
    김마루
  • 전체
    오늘
    어제
    • 분류 전체보기 N
      • 대외 활동
        • 오픈소스 개발자 대회
        • LG 유레카
      • Project
        • Web
        • App
      • CS
        • DB
        • OS
      • Algorithm
      • Language
        • Java
        • Spring
      • etc
      • Certification N
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    자바
    부트캠프후기
    유레카백엔드
    멀티캠퍼스IT부트ㅐㅁ프
    부트캠프
    오픈소스 개발자대회
    N-Queen
    멀티캠퍼스IT부트캠프
    KDT
    LG유플러스유레카백엔드개발자
    유레카 부트캠프
    BFS
    멀티캠퍼스 부트캠프
    BufferedReader
    유레카백엔드개발자
    SQL
    유레카3기
    MySQL
    유레카
    알고리즘
    멂티캠퍼스IT부트캠프
    백트래킹
    부트탬프후기
    멀티캠퍼스부트캠프
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
김마루
[Java] BufferedReader vs scanner
상단으로

티스토리툴바