알고리즘/기본코드

[입출력]01. Scanner

은서파 2024. 11. 27. 18:25

이번 포스트에서는 자바에서 입력을 가장 손쉽게 처리할 수 있는  Scanner에 대해서 알아보자.

 

Scanner

 

기본 사용법

Scanner를 만들기 위한 생성자는 InputStream 타입의 파라미터 source를 받는다.

public Scanner(InputStream source) {...}

이 source가 어디에서 자료를 받을것인지를 결정하는데 일반적으로 키보드에서 받을 경우 System.in, 파일로 부터 받을 경우 FileInputStream이 사용된다.

 

Scanner에서 데이터를 읽어들일 때는 일반적으로 hasNextXXX() --> nextXXX()의 두 단계를 거친다.

여기서 XXX는 데이터 형에 따라 여러 가지 형태가 있다. 각각의 데이터 즉 토큰에 대한 구분은 공백문자('\t',space, '\n')를 사용한다.

 

예를 들어 다음은 키보드로부터 문자열을 입력받아서 출력하는 예이다.

private static void read0() {
	Scanner scanner = new Scanner(System.in);
	while (scanner.hasNext()) { 		// 다음 문자가 있는가?
		String next = scanner.next();	// 다음 토큰을 가져옴
		if (next.equalsIgnoreCase("x")) {
			break;
		} else {
			System.out.println(next);
		}
	}
	scanner.close(); // Scanner 닫기
}

 

위 코드를 실행하고 아래 주석에 해당하는 값을 입력시켜보자. [↩︎]는 엔터 키이다.

//hello[↩︎]
hello
//java[↩︎]
java
//world[↩︎]
world
//scanner is easy[↩︎]
scanner
is
easy

 

hello, java, world는 한 문장당 하나의 단어가 입력되었고 각 단어가 하나의 토큰이다. scanner is easy는 한 문장에 3개의 단어가 입력되었고 역시 각 단어가 하나의 토큰이다.

 

 

다양한 입력 소스

Scanner는 여러가지 입력 소스에서 데이터를 받을 수 있다.  앞선 예에서 키보드인  System.in을 사용해봤는데 이번에는 File에 저장된값을 읽어서 처리해보자. 파일에서 값을 읽어올 때는 노드스트림인 FileInputStream을 사용할 수 있다.

 

위 예에서 키보드에서 입력했던 내용을 c:/Temp/sample_input.txt파일에 작성하고 읽어보자.

hello
java
world
scanner is easy

 

private static void read1() throws FileNotFoundException {
	// 파일에 연결된 InputStream으로 Scanner 생성
	Scanner scanner = new Scanner(new FileInputStream("c:/Temp/sample_input.txt"));
	while (scanner.hasNext()) { 
		String next = scanner.next();
		if (next.equalsIgnoreCase("x")) {
			break;
		} else {
			System.out.println(next);
		}
	}
	scanner.close();
}

소스를 보면 다른 부분은 모두 동일하다 단지 Scanner를 생성할 때 키보드가 아닌 파일에서 가져왔을 뿐이다.

 

알고리즘 문제를 풀다보면 입력 값이 길게 주어지는 경우가 많은데 매번 입력하기 보다는 파일로 작성한 후 읽어들이면 손쉽게 입력을 처리할 수 있다.

 

하지만 파일을 만들고 읽어들이는 것도 상당히 귀찮은 일이다. 알고리즘 문제 풀이 시 가장 편리한 입력 소스는 String을 사용하는 것이다. 문제에서 제공되는 문자열을 복사 후 클래스에 붙여놓고 이 String을 소스로 Scanner를 생성해보자.

 

private static void read2() throws FileNotFoundException {
	// 문자열을 입력으로 Scanner 생성
	Scanner scanner = new Scanner(src);
	while (scanner.hasNext()) { 
		String next = scanner.next();
		if (next.equalsIgnoreCase("x")) {
			break;
		} else {
			System.out.println(next);
		}
	}
	scanner.close();
}
	
private static String src = "hello\r\n" + 
			 "java\r\n" + 
			 "world\r\n" + 
			 "scanner is easy";

 

다양한 자료형 처리

Scanner의 장점은 원하는 자료형에 맞춰 읽을 수 있다는 점이다.

앞서 method를 소개할 때 hasNextXXX(), nextXXX()라고 했던 점을 상기시켜보자.

다음은 Scanner가 갖는 hasNext 계열의 메서드들이다.

당연히 next 계열의 메서드들도 변수 타입에 따라 준비되어있다.

대부분 토큰 단위로 읽어들이고 hasNextLine()과 nextLine()은 한 줄씩 데이터를 읽어들이는 부분이 다르다.

문장단위로 읽어들인 데이터는 StringTokenizer나 String의 split() 메서드를 이용해서 토큰 단위로 쪼개서 사용한다. 이 두 녀석의 사용법은 다음 포스트에서 다룬다.

 

다음 예는 문자열 형태의 숫자들을 읽어오는 예이다.

// 정수가 아닌 것이 들어오면 반복 종료
private static void read3() {
	Scanner scanner = new Scanner(nums);
	while (scanner.hasNextInt()) {
		int next = scanner.nextInt();
		System.out.println(next);
	}
	scanner.close();
}

private static String nums = "100\r\n" + 
 		"200 300 400\r\n" + 
 		"500\r\n" + 
 		"x";

 

패턴의 사용

Scanner는 자동으로 입력값을 토큰으로 구분해서 읽어들인다. 이때 활용되는 구분자는 공백문자 즉 space, tab, carriage return이다. 하지만 필요에 따라서 쉼표등 구분자를 이용해야할 때도 있다. 이때는 Scanner의 useDelimiter()를 이용해 구분자로 사용할 패턴을 지정할 수 있다.

 

다음 예를 살펴보자.

private static void read4() {
	// , (쉼표와 공백)을 구분자로 갖는 데이터 처리
	Scanner scanner = new Scanner("100, 200, 300, 400");
	scanner.useDelimiter(", ");		// 사용자 정의 구분자 지정
	while (scanner.hasNextInt()) {
		int next = scanner.nextInt();
		System.out.println(next);
	}
	scanner.close();
}

 

위 예는 쉼표와 공백을 구분자로 갖는 일련의 데이터에서 필요한 토큰들을 추출해서 사용할 수 있게 한다.