본문 바로가기

개발새발 개발자/Java

[Java] 콘솔 입력 - InputStream, BufferedReader, Scanner

하던 일만 하다 보면 범위 밖에 있는 기초적인 내용은 어느새 잊어버리게 됩니다. 이번에는 Stream으로 입력을 받아오려다 크게 헤매서 정리해보려고 합니다.

 

1. InputStream

1996년 JDK 1.0에서 도입된 입출력의 조상님(...)입니다. java.io 패키지에 속해있어요.

 

1) InputStream: 1byte 읽기

import java.io.InputStream;

public class StreamTest {
    public static void main(String[] args) throws Exception {
    
        InputStream in = System.in;

        int a;
        a = in.read();

        System.out.println(a);
    }
}

InputStream 객체의 System.in 객체를 사용해서, read() 메소드로 입력을 받습니다.

 

여기서 주의할 점! read() 메소드는 1byte의 int 자료형으로 저장됩니다. 이 int 값은 아스키코드값이기 때문에 숫자를 입력해도 아스키 코드 값으로 출력됩니다.

 

1	// 입력값
49	// 출력값

위의 코드를 실행하면 숫자 1을 입력해도 아스키코드 49로 출력이 되는 걸 볼 수 있어요.

 

abc	// 입력값
97	// 출력값

이번엔 abc를 입력해봅니다. abc 세 문자에 대한 아스키코드가 나와야 하는데, a의 값 97만 나왔네요. read() 메소드는 1byte만 읽기 때문에 3byte를 입력해도 나머지 2byte는 처리하지 못한 겁니다.

 

그렇다면 3byte를 모두 읽으려면 어떻게 해야할까요?

import java.io.InputStream;

public class Main {
    public static void main(String[] args) throws Exception {
    
        InputStream in = System.in;

        byte[] a = new byte[3];
        in.read(a);

        System.out.println(a[0]);
        System.out.println(a[1]);
        System.out.println(a[2]);
    }
}

length가 3인 배열을 만들어 read() 메소드를 호출할 때 입력값으로 전달하면 됩니다.

 

abc	// 입력값
97
98
99	// 출력값

그럼 abc 총 3byte에 해당하는 아스키 코드를 출력하는 걸 볼 수 있습니다! 하지만 아스키코드로 표현되는 건 여전히 불편하네요.

 

2) InputStreamReader: 문자로 읽기

java.io 패키지의 InputStreamReader 클래스를 사용해봅시다!

 

import java.io.InputStream;
import java.io.InputStreamReader;

public class Main {
    public static void main(String[] args) throws Exception {
    
        InputStream in = System.in;
        InputStreamReader reader = new InputStreamReader(in);
        
        char[] a = new char[3];
        reader.read(a);

        System.out.println(a);
    }
}

전체 코드에서 InputStreamReader를 살펴볼게요.

 

InputStreamReader reader = new InputStreamReader(in);

InpuStreamReaer는 InputStream 객체를 입력으로 가지고 있어야 해요. 그래서 앞에서 생성한 in을 넣어줬습니다.

 

char[] a = new char[3];

또 다른 점! 아까는 byte로 선언했는데 이제 드디어!!! char로 받을 수 있습니다.

 

abc	// 입력
abc	// 출력

코드를 실행하면 이렇게 똑같이 출력되는 걸 볼 수 있어요.

 

3. BufferedReader: 통째로 읽기

InputStreamReader 덕분에 글자 그대로 읽을 수 있게 됐지만, 또 하나 불편한 점이 있어요. 배열 크기를 일일이 정해줘야한다는 점입니다. 얼마나 길게 입력할지 모르는데 무한정 크게 배열을 잡아놓을 수는 없잖아요 T.T

 

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;

public class StreamTest {
    public static void main(String[] args) throws Exception {
    
        InputStream in = System.in;
        InputStreamReader reader = new InputStreamReader(in);
        BufferedReader br = new BufferedReader(reader);

        String a = br.readLine();
        System.out.println(a);
    }
}

그래서 등장한 것이 BufferedReader 입니다. BufferedReader는 InputStreamReader의 객체를 입력값으로 사용해요.

 

Hello World	// 입력값
Hello World	// 출력값

코드를 실행하면 이렇게 정상적으로 문자열이 출력됩니다.

 

Reads a line of text. A line is considered to be terminated by any one of a line feed ('\n'), a carriage return ('\r'), or a carriage return followed immediately by a linefeed.

Returns: A String containing the contents of the line, not including any line-termination characters, or null if the end of the stream has been reached.

BufferedReader의 공식 문서를 읽어볼게요.

 

BufferedReader는 텍스트의 라인을 읽는데, 라인이라 함은 \n처럼 엔터로 새로운 줄이 생길 때를 의미한대요. 즉, 엔터를 치기 전까지는 모든 텍스트를 BufferedReader에 저장하는거죠.

 

반환값은 String 입니다. \n처럼 줄이 끝나거나, stream이 꽉 차서 null을 반환하는 게 아니라면 계속 그 값을 가지고 있는 것 같아요.

 

4) 정리

어휴, 문장 하나 받는데 뭐가 이렇게 많이 필요하죠? 다시 한 번 깔끔하게 정리해볼게요!

InputStream byte InputStream in = System.in; read()
InputStreamReader char InputStreamReader reader = new InputStreamReader(in); read()
BufferedReader String BufferedReader br = new BufferedReader(reader); readLine()

아래로 차곡차곡 감싸고 또 감싼다는 것도 기억해두세요!

 

BufferedReader는 데이터가 String으로만 인식되고 사용이 복잡하지만 많은 데이터를 입력받을 경우에는 아래에서 살펴볼 Scanner보다 메모리 사용면에서 효율적입니다.

 

2. Scanner

자바 입출력을 다룰 때 제일 먼저 배우는 클래스입니다. 2004년에 release된 J2SE 5.0부터 java.util.Scanner 클래스가 새로 추가 되면서 이전보다 콘솔 입력을 쉽게 처리할 수 있게 되었습니다. InputStream이 96년에 태어났으니 Scanner는 정말 최신이네요 :D

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
    
        Scanner sc = new Scanner(System.in);
        System.out.println(sc.next());
        
    }
}

Scanner 클래스는 System.in을 필요로 합니다. System.in은 앞에서 배운 InputStream의 객체이죠! 

 

Scanner는 next() 메소드로 단어 하나(token)을 읽습니다.

  • next: 단어
  • nextLine: 라인
  • nextInt: 정수

앞에서 살펴보았던 InputStream보다 훨씬 더 간단하게 사용할 수 있는 게 보이시죠? InputStream 쓸 때의 고구마 답답이가 뻥 뚫리는 기분이네요 :)

 

 

※ 참고자료: 점프 투 자바