[Java] Scanner 클래스 버퍼 비우기

📁 Scanner

Scanner는 사용자로부터 편리하게 입력을 받기 위한 목적으로 JDK1.5부터 추가된 클래스이다. 

자바 학습 초기에 특히 유용하게 사용했던 클래스인데, 사용 시 주의해야 할 부분이 있다.

 

import java.util.*;

public class Main {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		
		String name = sc.nextLine();
		int age = sc.nextInt();
		String job = sc.nextLine();
		
		System.out.printf("%s(%d, %s)", name, age, job);
	}
}

nextLine(), nextInt() 메서드를 사용해 사용자로부터 이름과 나이, 직업을 입력받은 후, 이를 정규식 형태로 출력하는 코드이다.

평범한 코드같지만 실행해보면 치명적인 문제가 있다.

 

이름과 나이를 입력하고 이제 직업을 입력할 차례인데

szzzing(20, )

백수의 ㅂ자 칠 틈조차 안주고 job은 빈 문자열로 처리된다.

왜 이런 문제가 발생하는 걸까?

📁 버퍼 Buffer

RAM에 존재하는 임시 저장소

버퍼는 RAM에 존재하는 임시 저장소로 큐(Queue)의 구조를 가지고 있다.

사용자가 콘솔을 통해 값을 입력하면 그 값은 버퍼에 임시 저장된다.

 

이름, 나이 등 2개 이상의 값을 입력할 때에는 엔터나 스페이스 키를 눌러 변수의 경계를 구분한다.

그런데 여기서 공백과 줄바꿈은 다른 숫자, 문자와 마찬가지로 버퍼에 하나의 값으로 저장된다.

이제 Scanner 객체는 next()와 같은 메서드를 통해 버퍼에 저장된 값을 앞에서부터 차례대로(FIFO) 꺼내온다.

 

문제는 메서드들이 버퍼에 저장된 값 중 어떤 것을 가져오는지에 있다.

Scanner 클래스 대부분의 메서드들은 입력값 중 공백이나 개행문자를 기준으로 그 앞의 자료형 값만을 가져오고, 나머지는 버퍼에 남겨둔다.

그러나 nextLine()은 공백과 개행문자를 포함하여 현재 버퍼에 남아있는 모든 값들을 가져온다.

 

다시 우리의 코드를 살펴보자.

String name = sc.nextLine();
int age = sc.nextInt();
String job = sc.nextLine();
  1. 이름을 입력하면 nextLine()은 버퍼에 있는 모든 데이터를 가져와 name에 저장한다. 현재 버퍼는 비어있다.
  2. 나이를 입력하고 엔터를 누르면 nextInt()는 개행문자를 제외한 int값을 age에 저장한다. 버퍼에는 개행문자가 남아있다.
  3. nextLine()은 버퍼에 개행문자가 남아있는 것을 보고 이미 입력을 받은 것으로 판단한다. 따라서 추가적으로 입력을 받지 않고 job에 빈 문자열을 저장한 것이다.

📁 버퍼 비우기

심각한 에러는 아니지만, 어쨌든 의도한 프로그램과 다른 방식으로 흘러가니 수정이 필요하다.

nextLine()이 호출되기 전 비워지지 않은 버퍼 때문에 발생한 문제이니, 버퍼를 비워야 해결될 것이다.

방법은 두가지가 있다.

☝️ nextLine() 삽입

nextInt()가 작업을 수행한 후 남은 버퍼를 비워주기 위해 nextLine()을 사용한다.

nextLine()이 버퍼에 남아있는 공백 및 개행문자를 가져가줌으로써 새로 입력받을 수 있는 상태를 만들어주는 것이다.

String name = sc.next();
int age = sc.nextInt();
sc.nextLine();	//	버퍼를 비워주는 용도
String job = sc.nextLine();

✌️ 일관적인 메서드 사용

공백/개행문자를 가져가는 nextLine() 메서드만을 일관적으로 사용하거나, 아니면 아예 사용하지 않는 방법이다.

 

String name = sc.next();
int age = sc.nextInt();
String job = sc.next();

전자는 항상 버퍼를 비워둘 수 있는 방법이다. 문자열을 입력받을 때에는 nextLine() 대신 next()를 사용한다.

다만 nextLine()은 개행문자를 기준으로 하는 반면 next()는 공백문자를 기준으로 값을 가져온다는 차이가 있어 주의가 필요하다.

 

String name = sc.nextLine();
int age = Integer.parseInt(sc.nextLine());
String job = sc.nextLine();

후자는 공백/개행문자가 차후 입력에 영향을 미치지 못하도록 원천 차단하는 방법이다.

nextLine()은 문자열을 리턴하므로, 문자열을 기본 자료형으로 파싱해주는 메서드를 사용하여 값을 알맞게 저장해주어야 한다.