프로그램을 실행했더니 다음과 같은 문자열이 출력되었다고 가정해 본다.
생년월일(YYYY/MM/DD)을 입력해 주세요:
이렇게 사용자에게 위와 같은 문자열을 보여주는 것이 바로 콘솔 출력이고 위 질문에 사용자가 답변을 입력하는 것을 콘솔 입력이라 한다.
※ 콘솔이란?
콘솔은 환경에 따라 변경될 수 있다. 만약 이 프로그램을 IDE에서 실행했다면 IDE콘솔탕이 콘솔이 될 것이고 윈도우 명령창에서 이프로그램을 실행했다면 명령창이 콘솔이 된다. 즉, 콘솔은 사용자의 입력을 받거나 사용자에게 문자열을 출력해 주는 역할을 하는 것을 통칭하는 말이다.
콘솔 입력
위와 같은 콘솔 출력 질문에 1999/09/09 같은 답변을 키보드로 입력할 것이다. 이렇게 입력한 문자열이 바로 콘솔 입력에 해당한다. 자바 코드에서 위와 같이 입력한 문자열을 얻기 위해서는 System.in을 사용한다.
import java.io.IOException;
import java.io.InputStream;
public class Sample {
public static void main(String[] args) throws IOException {
InputStream in = System.in;
int a;
a = in.read();
System.out.println(a);
}
}
위에서 사용한 InputStream은 자바의 내장 클래스이다. 자바의 내장 클래스중에 java.lang패키지에 속해 있지 않은 클래스는 위 코드처럼 필요할 때 항상 import해서 사용해야 한다. System이나 String등의 클래스는 java.lang패키지에 속해 있는 클래스이므로 별도의 import과정이 필요 없다.
다음 문장에서 알 수 있듯이 System.in은 InputStream의 객체임을 알 수 있다.
InputStream in = System.in;
InputStream의 read메소드는 다음처럼 1 byte의 사용자의 입력을 받아들인다.
int a;
a = in.read();
하지만 read 메소드로 읽은 1 byte의 데이터는 byte 자료형으로 저장되는 것이 아니라 int 자료형으로 저장된다. 저장되는 int 값은 0-255 사이의 정수값으로 아스키 코드값이다.
※ 0 이라는 숫자에 해당되는 아스키코드값은 48, a 라는 문자에 해당되는 아스키코드값은 97이다.
이제 프로그램을 실행시켜보면 프로그램은 종료되지 않고 사용자의 입력을 대기하고 있을 것이다. 왜냐하면 InputStream의 read()메소드가 호출되면 사용자의 입력을 받을 때까지 프로그램이 대기하기 때문이다.
이제 콘솔 창에 'a' 키를 입력한다. 아무런 반응이 없을 것이다. 그리고 다시 "엔터"키를 입력한다.
※ 엔터키를 입력해야 사용자의 입력이 종료되고 프로그램에 전달된다.
다음과 같이 출력이 되고 프로그램은 종료될 것이다.
97
97은 사용자가 입력한 문자인 a에 해당하는 아스키 코드값이다.
IOException
위 예제에 main 메소드를 보면 throws IOException을 사용한 부분이 있다. InputStream으로 부터 값을 읽어들일 때는 IOException이 발생할 수 있기 때문에 예외처리를 해야 하는데 throws로 그 예외처리를 뒤로 미루게 한 것이다.
InputStream
다시 프로그램을 실행시키고 이번엔 "abc"를 연속해서 입력한 후 "엔터"키를 다시 입력한다.
이번에도 97이 출력될 것이다.
"abc" 를 입력했지만 출력은 "a"에 해당되는 아스키 코드값만 출력되었다. 그 이유는 InputStream의 read 메소드는 1 byte만 읽기 때문이다. 즉, 사용자는 "abc"라는 총 3 byte의 데이터를 전달했지만 프로그램에서 1 byte만 읽은 경우라고 할 수 있다. 이렇게 사용자가 전달한 1 byte의 데이터 또는 3 byte의 데이터를 다른 말로 입력 스트림(Stream)이라고 한다. 스트림은 이어져 있는 데이터(byte)의 형태를 의미한다.
스트림(Stream)이란?
스트림을 가장 쉽게 이해하려면 수도꼭지를 생각하면 된다. 수도꼭지를 틀면 물이 나오고 수도꼭지를 잠그면 물이 나오지 않는다. A라는 곳에서부터 B라는 곳까지 수도관이 연결되어 있고 A에서 계속 물을 보낸다면 B에서 수도꼭지를 틀때마다 물이 나오게 될 것이다. 여기서 스트림은 A수도관에서 B수도관으로 이동하는 물의 흐름이라고 할 수 있다.
프로그래밍에서는 다음과 같은 것들을 스트림이라고 할 수 있다.
- 파일 데이터 (파일은 그 시작과 끝이 있는 데이터의 스트림이다.)
- HTTP 송수신 데이터 (브라우저가 요청하고 서버가 응답하는 HTTP 형태의 데이터도 스트림이다.)
- 키보드 입력 (사용자가 키보드로 입력하는 문자열은 스트림이다.)
사용자가 3byte를 입력했을 때 3byte를 전부 읽고 싶다면 다음처럼 예제를 작성한다.
import java.io.IOException;
import java.io.InputStream;
public class Sample {
public static void main(String[] args) throws IOException {
InputStream in = System.in;
int a;
int b;
int c;
a = in.read();
b = in.read();
c = in.read();
System.out.println(a);
System.out.println(b);
System.out.println(c);
}
}
위 예처럼 read() 메소드를 3번 실행하도록 수정하고 프로그램을 다시 실행해 보면 "abc" 입력시 총 3 byte를 읽어들이는 것을 확인할 수 있을 것이다.
abc (입력 + 엔터)
97 (출력)
98 (출력)
99 (출력)
또는 다음과 같이 좀 더 개선된 방법을 사용할 수 있다.
import java.io.IOException;
import java.io.InputStream;
public class Sample {
public static void main(String[] args) throws IOException {
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]);
}
}
길이 3 짜리 byte배열을 만든 후 read 메소드의 입력값으로 전달하면 콘솔 입력이 해당 배열에 저장이 된다. 프로그램을 실행해 보면 이전과 동일한 결과가 출력되는것을 확인 할 수 있다.
abc (입력)
97 (출력)
98 (출력)
99 (출력)
InputStreamReader
하지만 읽어들인 값을 항상 아스키코드 값으로 해석해야 하는 이 방식은 여전히 불편하다. 입력한 문자값을 그대로 출력하는 방법이 있다. 바이트 대신 문자로 입력 스트림을 읽으려면 InputStreamReader를 사용하면 된다.
위 예제를 다음과 같이 변경한다.
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class Sample {
public static void main(String[] args) throws IOException {
InputStream in = System.in;
InputStreamReader reader = new InputStreamReader(in);
char[] a = new char[3];
reader.read(a);
System.out.println(a);
}
}
InputStreamReader를 사용하기 위해 import 문이 하나 더 추가되었다. 그리고 InputStreamReader 객체를 생성할 때는 다음과 같이 생성자의 입력으로 InputStream 객체가 필요하다.
InputStreamReader reader = new InputStreamReader(in);
이전에는 읽어들일 값을 byte배열로 선언했는데 InputStreamReader를 이용하면 다음처럼 byte 대신 char 배열을 사용할 수 있다.
char[] a = new char[3];
프로그램을 실행하고 "abc" 입력 후 엔터키로 사용자 입력을 전달하면 다음과 같이 "abc"라는 문자열이 한꺼번에 출력되는 것을 확인할 수 있다.
abc (입력)
abc (출력)
BufferedReader
프로그램이 많이 개선되었지만 여전히 불편한점은 남아있다. 불편한 점은 고정된 길이로만 스트림을 읽어야 한다는 점이다. 위 예제는 항상 3 byte만 읽도록 고정되어 있다.
사용자가 엔터키를 입력할 때까지 사용자의 입력을 전부 받아들이려면 BufferedReader를 이용하면 가능하다.
다음과 같이 예제를 수정한다.
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
public class Sample {
public static void main(String[] args) throws IOException {
InputStream in = System.in;
InputStreamReader reader = new InputStreamReader(in);
BufferedReader br = new BufferedReader(reader);
String a = br.readLine();
System.out.println(a);
}
}
역시 BufferedReader를 이용하기 위해 import 문이 추가되었다. BufferedReader는 객체 생성시 생성자의 입력값으로 InputStreamReader의 객체가 필요하다. 이제 BufferedReader의 readLine메소드를 이용하면 사용자가 엔터키를 입력할 때까지 입력했던 문자열 전부를 읽을 수 있게 된다. 프로그램을 실행하고 "Hello World"라고 입력한 후 엔터키를 입력하면 "Hello World"라는 문자열이 그대로 Echo되어 출력되는 것을 확인할 수 있을 것이다.
Hello World (입력)
Hello World (출력)
- InputStream - byte
- InputStreamReader - character
- BufferedReader - String
Scanner
J2SE 5.0 부터 Scanner 라는 java.util.Scanner 클래스가 새로 추가되었다. Scanner 클래스를 이용하면 콘솔입력을 보다 쉽게 처리 할 수 있다.
import java.util.Scanner;
public class Sample {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println(sc.next());
}
}
Scanner 를 사용하기 위해서는 먼저 java.util.Scanner 클래스를 import 해야 한다.
import java.util.Scanner;
Scanner 클래스는 생성자의 입력으로 System.in, 즉 콘솔입력인 InputStream을 필요로 한다.
Scanner 객체의 next() 메소드는 단어 하나(Token)를 읽어들인다. Scanner 클래스에는 단어 뿐만 아니라 숫자, 문자열등 다양하게 읽어 들일 수 있는 여러 메소드들이 준비되어 있는데 몇가지만 알아보면 다음과 같은 것들이 있다.
- next - 단어
- nextLine - 라인
- nextInt - 정수
콘솔 출력
예제들에서 System.out.println 메서드를 계속해서 사용해 왔다. System.out은 PrintStream 클래스의 객체이다. PrintStream은 콘솔에 값을 출력할 때 사용되는 클래스이다. 보통 System.out.println은 콘솔에 문자열을 출력할 경우나 디버깅 시 많이 사용된다.
System.err도 있는데 System.out과 동일한 역할을 한다. 다만 System.err는 오류메시지를 출력할 경우에 사용한다.
Unix 콘솔의 System.out과 System.err
Unix의 경우 콘솔 프로그램 실행시 출력 옵션을 지정하면 System.out으로 출력한 내용과 System.err로 출력한 내용을 별도의 파일로 저장할수 있다.
public class Sample {
public static void main(String[] args) {
System.out.println("일반 출력");
System.err.println("에러 출력");
}
}
위와 같이 작성한 Sample.java 소스를 컴파일하여 Sample.class 파일을 생성한 후 유닉스에서 다음처럼 실행하면 out.txt 파일에는 "일반 출력" 이라는 문자열이 저장되고 error.txt 파일에는 "에러 출력"이라는 문자열이 저장된다.
$ java Sample > out.txt 2> error.txt
※ 유닉스에서 > 는 일반 출력, 2> 는 오류 출력에 해당된다.
'Language > Java' 카테고리의 다른 글
[Java] 패키지 (Package) (0) | 2022.08.04 |
---|---|
[Java] 파일 입출력 (0) | 2022.08.04 |
[Java] 추상클래스 (0) | 2022.08.02 |
[Java] 다형성 (0) | 2022.08.02 |
[Java] 인터페이스 (Interface) (0) | 2022.08.02 |