빌려온 가나디

코딩 테스트에서 자바 입출력 받기 (BufferedReader, StringTokenizer, java.util.NoSuchElementException) 본문

Java

코딩 테스트에서 자바 입출력 받기 (BufferedReader, StringTokenizer, java.util.NoSuchElementException)

daun_up 2026. 1. 31. 18:16

파이썬으로 코딩 테스트를 풀다가 SSAFY 과정 덕에 자바로 언어를 바꾸게 되었다.

어떻게든 쉽게 가고 싶어서 버티다가 져버림... 

Scanner

자바에서 입력을 받을 때는

Scanner sc = new Scanner(System.in); 을 사용했었다.

입력을 읽고 공백/줄바꿈 기준으로 쪼개고 int, double, String 으로 자동 변환하는 것을 한 번에 해준다고 한다.

 

내부적으로는 아래와 같은 과정을 거친다.

  1. InputStream에서 문자 단위로 읽음
  2. 정규식(regex)으로 구분자 처리
  3. 타입 검사 (int인지, double인지)
  4. 파싱 실패 시 예외 처리

딱 봐도 많은 걸 해주고 있다는 것은 무겁다는 뜻이다! 입력 개수가 많고 시간 제한이 빡빡할 때는 쓰기가 어렵다.

Scanner 를 썼을 때는 10 20 30 40 50 이라는 입력이 주어졌을 때 int a = sc.nextInt(); 로 하나 읽고, 또 다음 b 읽고 문자 단위로 입력을 읽어들인다. '1' → '0' → ' ' → '2' → '0' → ' ' → ... 이렇게 공백 기준으로 한 글자씩 읽는데 기본 구분자 자체가 정규식이고, 토큰 분리 과정에 정규식 매칭을 동반한다.

BufferedReader + StringTokenizer

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

 

BufferedReader 입력 방식을 사용했을 때는, 한 줄을 통째로 읽어들이기 때문에 StringTokenizer 를 추가로 사용해주어야 한다.

"10 20 30 40 50" 이렇게 한 줄이 입력되어 내부적으로 버퍼(배열)을 사용한다.

해당 줄을 StringTokenizer 가 스캔하고 공백 기준으로 위치를 기억한다. 문자열을 실제로 쪼개는 것이 아니라, 인덱스만 이동한다.

그래서 st.nextToken(); 를 호출하면 포인터가 이동되고, 문자열이 반환된다. 그래서 int, double 등을 입력 받을 때 int a = Integer.parseInt(st.nextToken()); 이렇게 형 변환을 해주어야 한다.

java.util.NoSuchElementException

근데 자꾸 이런 에러가 났다. nextToken()을 호출했는데 더 이상 토큰이 없을 때 나는 에러라고 한다.

즉, 입력 개수 가정이 틀리거나(토큰 부족), 빈 줄이 들어와 토큰이 0개인데 nextToken()을 호출하면 예외가 난다.

근데 난 그런 적이 없다고 생각했다... 그러면 왜 이런 상황이 생기는가? 그 이유는 st 를 재사용했기 때문이다.  

5
3.0 2.8 4.0 3.5 3.9

이런 입력을 받을 때 위에 n 도 st 로 받아버리고 싶었다!

int n = Integer.parseInt(br.readLine()); 한 개이기 때문에 이렇게 받아도 되지만 만약 st 로 받고자 한다면?

// 문제가 된 코드!

StringTokenizer st = new StringTokenizer(br.readLine());

int n = Integer.parseInt(st.nextToken());
double sum = 0;

for (int i = 0; i < n; i++) {
    sum += Double.parseDouble(st.nextToken());
}

java.util.NoSuchElementException 예외가 생긴 코드는 이 코드이다.

  • br.readLine()
    • 첫 줄 "5"를 읽음
  • st = new StringTokenizer("5")
    • st 안 토큰: [ "5" ]
  • int n = Integer.parseInt(st.nextToken());
    • "5" 꺼냄 → n = 5 가 되고, 이제 st는 토큰이 0개이다.
  • sum += Double.parseDouble(st.nextToken());
    • for문 시작!! 그 런 데 st에 더 이상 토큰이 없음... 그래서 st.nextToken()에서 바로 예외 발생
StringTokenizer st = new StringTokenizer(br.readLine());
int n = Integer.parseInt(st.nextToken());

st = new StringTokenizer(br.readLine());
for (int i = 0; i < n; i++) {
    sum += Double.parseDouble(st.nextToken());
}

그래서 이와 같이 재생성 해주어야 한다. StringTokenizer 는 줄이 바뀌면 다시 만들어줘야 한다! 

 

그리고 추가로 안전 장치를 걸어둘 수도 있다.

hasMoreTokens()

이 메서드로 nextToken 을 호출하기 전에 항상 미리 확인한다.

while (st.hasMoreTokens()) {
    int num = Integer.parseInt(st.nextToken());
}

'Java' 카테고리의 다른 글

올바른 싱글톤을 구현하는 방법  (0) 2026.02.08