Ch 2.6 화면 입출력 (Console I/O)
지금까지 우리는 변수에 직접 값을 대입하여 화면에 정보를 출력해보았습니다. 이번 챕터에서는 원하는 형식으로 문장을 출력 하는 방법과 반대로 사용자가 키보드로 입력한 값을 읽어다가 변수에 저장 하는 방법을 배웁니다.
1. 출력 메서드 세 가지 비교
자바에서 콘솔에 출력하는 방법은 세 가지입니다.
| 메서드 | 줄바꿈 | 사용처 |
|---|---|---|
System.out.println() | 자동 줄바꿈 | 일반 출력 |
System.out.print() | 줄바꿈 없음 | 같은 줄에 이어서 출력 |
System.out.printf() | 없음 (\n 직접 추가) | 형식 지정 출력 |
public class OutputMethods {
public static void main(String[] args) {
// println: 출력 후 자동 줄바꿈
System.out.println("첫 번째 줄");
System.out.println("두 번째 줄");
System.out.println(); // 빈 줄 출력
// print: 줄바꿈 없이 이어서 출력
System.out.print("A");
System.out.print("B");
System.out.print("C");
System.out.println(); // 줄바꿈 추가
// printf: 형식 지정 출력
System.out.printf("이름: %s, 나이: %d세%n", "Alice", 20);
// %n은 플랫폼에 맞는 줄바꿈 (\n과 동일하지만 이식성 높음)
}
}
출력:
첫 번째 줄
두 번째 줄
ABC
이름: Alice, 나이: 20세
2. printf와 형식 지정자 (Format Specifier) 완전 정리
printf는 C언어에서 유래한 형식 지정 출력 메서드입니다. % 기호 뒤에 타입을 나타내는 문자를 붙여 사용합니다.
기본 형식 지정자
| 지정자 | 타입 | 설명 | 예시 |
|---|---|---|---|
%d | 정수 | 10진수 정수 | %d → 42 |
%o | 정수 | 8진수 정수 | %o → 52 |
%x | 정수 | 16진수 (소문자) | %x → 2a |
%X | 정수 | 16진수 (대문자) | %X → 2A |
%f | 실수 | 고정 소수점 | %f → 3.140000 |
%e | 실수 | 지수 표기법 (소문자) | %e → 3.140000e+00 |
%E | 실수 | 지수 표기법 (대문자) | %E → 3.140000E+00 |
%g | 실수 | 더 짧은 방식 자동 선택 | - |
%s | 문자열 | String | %s → "hello" |
%S | 문자열 | 대문자 String | %S → "HELLO" |
%c | 문자 | char | %c → A |
%b | 논리 | boolean | %b → true |
%n | - | 줄바꿈 (플랫폼 독립) | - |
%% | - | % 문자 자체 | %% → % |
public class PrintfBasic {
public static void main(String[] args) {
int intVal = 42;
double dblVal = 3.14;
String strVal = "Java";
char charVal = 'J';
boolean boolVal = true;
System.out.printf("정수: %d%n", intVal);
System.out.printf("8진수: %o%n", intVal);
System.out.printf("16진수(소): %x%n", intVal);
System.out.printf("16진수(대): %X%n", intVal);
System.out.printf("실수: %f%n", dblVal);
System.out.printf("지수: %e%n", dblVal);
System.out.printf("문자열: %s%n", strVal);
System.out.printf("대문자: %S%n", strVal);
System.out.printf("문자: %c%n", charVal);
System.out.printf("논리: %b%n", boolVal);
System.out.printf("퍼센트: 50%%%n");
}
}
출력:
정수: 42
8진수: 52
16진수(소): 2a
16진수(대): 2A
실수: 3.140000
지수: 3.140000e+00
문자열: Java
대문자: JAVA
문자: J
논리: true
퍼센트: 50%
3. printf 플래그와 폭/정밀도 지정
형식 지정자에 추가 옵션을 붙여 출력 형식을 세밀하게 조정할 수 있습니다.
문법: %[플래그][폭][.정밀도]타입
| 플래그/옵션 | 의미 | 예시 |
|---|---|---|
숫자 | 출력 폭(너비) 지정 | %10d → 10자리 오른쪽 정렬 |
- | 왼쪽 정렬 | %-10d → 10자리 왼쪽 정렬 |
0 | 빈 자리를 0으로 채움 | %010d → 0000000042 |
+ | 부호 항상 출력 | %+d → +42 또는 -42 |
| 양수 앞에 공백 | % d → 42 |
, | 천 단위 구분자 | %,d → 1,000,000 |
.숫자 | 소수점 이하 자리수 | %.2f → 3.14 |
public class PrintfFlags {
public static void main(String[] args) {
int num = 42;
double d = 3.14159;
long big = 1_000_000;
// 폭 지정 (오른쪽 정렬 기본)
System.out.printf("[%10d]%n", num); // [ 42]
System.out.printf("[%-10d]%n", num); // [42 ]
// 0 패딩
System.out.printf("[%010d]%n", num); // [0000000042]
// 부호 표시
System.out.printf("[%+d]%n", num); // [+42]
System.out.printf("[%+d]%n", -num); // [-42]
// 천 단위 구분자
System.out.printf("[%,d]%n", big); // [1,000,000]
// 소수점 자리수
System.out.printf("[%.2f]%n", d); // [3.14]
System.out.printf("[%.5f]%n", d); // [3.14159]
System.out.printf("[%10.2f]%n", d); // [ 3.14]
System.out.printf("[%-10.2f]%n", d); // [3.14 ]
// 표 형식 출력 예제
System.out.printf("%-15s %5s %8s%n", "이름", "나이", "점수");
System.out.printf("%-15s %5d %8.2f%n", "김자바", 20, 95.5);
System.out.printf("%-15s %5d %8.2f%n", "이파이선", 22, 88.3);
System.out.printf("%-15s %5d %8.2f%n", "박씨샵", 21, 92.1);
}
}
출력:
[ 42]
[42 ]
[0000000042]
[+42]
[-42]
[1,000,000]
[3.14]
[3.14159]
[ 3.14]
[3.14 ]
이름 나이 점수
김자바 20 95.50
이파이선 22 88.30
박씨샵 21 92.10
4. Scanner 클래스 상세
Scanner는 키보드 입력을 읽는 가장 기본적인 방법입니다. 사용 전에 import java.util.Scanner;를 선언해야 합니다.
Scanner 주요 메서드
| 메서드 | 반환 타입 | 설명 |
|---|---|---|
next() | String | 공백 이전까지 한 단어 읽기 |
nextLine() | String | 한 줄 전체 읽기 (Enter까지) |
nextInt() | int | 정수 읽기 |
nextLong() | long | long 정수 읽기 |
nextDouble() | double | 실수 읽기 |
nextFloat() | float | float 실수 읽기 |
nextBoolean() | boolean | true/false 읽기 |
hasNext() | boolean | 다음 토큰 있는지 확인 |
hasNextInt() | boolean | 다음 토큰이 int인지 확인 |
import java.util.Scanner;
public class ScannerMethods {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 다양한 타입 입력받기
System.out.print("이름 입력: ");
String name = scanner.nextLine(); // "홍길동 ABC" 입력 시 전체 읽음
System.out.print("나이 입력: ");
int age = scanner.nextInt(); // 정수만 읽음
System.out.print("키 입력: ");
double height = scanner.nextDouble(); // 실수 읽음
System.out.printf("이름: %s, 나이: %d, 키: %.1f cm%n",
name, age, height);
scanner.close();
}
}
5. Scanner 사용 시 주의사항 (nextLine 공백 문제)
nextInt(), nextDouble() 등은 숫자만 읽고 줄바꿈 문자(\n)를 버퍼에 남겨둡니다. 이후 nextLine()을 호출하면 버퍼에 남은 \n을 읽어 빈 문자열이 반환됩니다.
import java.util.Scanner;
public class ScannerPitfall {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("나이 입력: ");
int age = scanner.nextInt(); // "25\n" 입력 → 25 읽음, \n은 버퍼에 남음
// 문제 발생!
// System.out.print("이름 입력: ");
// String name = scanner.nextLine(); // \n을 읽어서 빈 문자열 반환!
// 해결 방법 1: nextInt() 후에 빈 nextLine()으로 버퍼 비우기
scanner.nextLine(); // 버퍼에 남은 \n 소비
System.out.print("이름 입력: ");
String name = scanner.nextLine(); // 이제 정상 동작
System.out.printf("이름: %s, 나이: %d%n", name, age);
scanner.close();
}
}
경고
Scanner 주의사항:
nextInt()후nextLine()사용 시 버퍼 비우기 필수nextLine()버퍼 비우기:scanner.nextLine();한 번 더 호출- 또는
nextLine()만 사용하고Integer.parseInt()로 변환하는 방법도 있음
더 안전한 방법: nextLine + parseInt 조합
import java.util.Scanner;
public class ScannerSafeMethod {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 모든 입력을 nextLine()으로 받고, 필요시 형변환
System.out.print("이름 입력: ");
String name = scanner.nextLine();
System.out.print("나이 입력: ");
int age = Integer.parseInt(scanner.nextLine().trim());
System.out.print("키 입력: ");
double height = Double.parseDouble(scanner.nextLine().trim());
System.out.printf("이름: %s, 나이: %d, 키: %.1f cm%n",
name, age, height);
scanner.close();
}
}
6. hasNext를 활용한 반복 입력
import java.util.Scanner;
public class ScannerLoop {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int sum = 0;
int count = 0;
System.out.println("숫자를 입력하세요 (종료: 빈 줄):");
// hasNextInt(): 다음 입력이 정수인지 확인
while (scanner.hasNextInt()) {
int num = scanner.nextInt();
sum += num;
count++;
System.out.println(" 입력됨: " + num + " (합계: " + sum + ")");
}
if (count > 0) {
System.out.printf("총 %d개, 합계: %d, 평균: %.2f%n",
count, sum, (double) sum / count);
}
scanner.close();
}
}
7. BufferedReader를 활용한 빠른 입력
경쟁 프로그래밍이나 대량 데이터 처리 시 BufferedReader가 Scanner보다 훨씬 빠릅니다.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
public class BufferedReaderExample {
public static void main(String[] args) throws IOException {
// BufferedReader는 IOException을 던질 수 있으므로 예외 처리 필요
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.print("이름 입력: ");
String name = br.readLine(); // 한 줄 읽기
System.out.print("나이 입력: ");
int age = Integer.parseInt(br.readLine().trim()); // 정수 변환
System.out.print("키 입력: ");
double height = Double.parseDouble(br.readLine().trim());
System.out.printf("이름: %s, 나이: %d, 키: %.1f cm%n",
name, age, height);
br.close();
}
}
Scanner vs BufferedReader 비교
| 항목 | Scanner | BufferedReader |
|---|---|---|
| 속도 | 느림 | 빠름 |
| 사용 편의성 | 편리 (타입별 메서드) | 번거로움 (모두 String) |
| 예외 처리 | 불필요 | IOException 처리 필요 |
| 사용 적합 | 일반 입력 | 대량 데이터, 알고리즘 |
8. System.err와 System.in
public class SystemStreams {
public static void main(String[] args) {
// System.out: 표준 출력 스트림 (콘솔 화면)
System.out.println("일반 출력");
// System.err: 표준 에러 스트림 (보통 콘솔에 빨간색으로 표시됨)
System.err.println("에러 메시지!"); // 에러나 경고 출력에 사용
// System.in: 표준 입력 스트림 (키보드)
// Scanner와 BufferedReader에서 System.in을 사용함
System.out.println("System.in 타입: " + System.in.getClass().getName());
// 에러 출력 활용 예
int divisor = 0;
if (divisor == 0) {
System.err.println("[ERROR] 나누는 수가 0입니다!");
} else {
System.out.println("결과: " + (10 / divisor));
}
}
}
9. try-with-resources로 Scanner 닫기
자원(Scanner, BufferedReader 등)은 사용 후 반드시 닫아야 합니다. try-with-resources 구문을 사용하면 자동으로 닫힙니다.
import java.util.Scanner;
public class TryWithResources {
public static void main(String[] args) {
// try-with-resources: 블록 종료 시 자동으로 scanner.close() 호출
try (Scanner scanner = new Scanner(System.in)) {
System.out.print("이름 입력: ");
String name = scanner.nextLine();
System.out.print("나이 입력: ");
int age = Integer.parseInt(scanner.nextLine().trim());
System.out.printf("안녕하세요, %s님! (%d세)%n", name, age);
} catch (NumberFormatException e) {
System.err.println("나이는 숫자로 입력해주세요: " + e.getMessage());
}
// 이 시점에서 scanner는 자동으로 닫혀있음
}
}
10. 실전 예제: 학생 정보 입력 프로그램
import java.util.Scanner;
public class StudentInfoInput {
public static void main(String[] args) {
try (Scanner scanner = new Scanner(System.in)) {
System.out.println("╔════════════════════════════╗");
System.out.println("║ 학생 정보 입력 시스템 ║");
System.out.println("╚════════════════════════════╝");
// 기본 정보 입력
System.out.print("이름: ");
String name = scanner.nextLine().trim();
System.out.print("학번: ");
String studentId = scanner.nextLine().trim();
System.out.print("나이: ");
int age = Integer.parseInt(scanner.nextLine().trim());
System.out.print("학과: ");
String major = scanner.nextLine().trim();
// 과목 점수 입력
System.out.println("\n과목 점수를 입력하세요 (0~100):");
System.out.print("국어: ");
int korean = Integer.parseInt(scanner.nextLine().trim());
System.out.print("수학: ");
int math = Integer.parseInt(scanner.nextLine().trim());
System.out.print("영어: ");
int english = Integer.parseInt(scanner.nextLine().trim());
// 계산
int total = korean + math + english;
double average = (double) total / 3;
// 학점 계산
char grade;
if (average >= 90) grade = 'A';
else if (average >= 80) grade = 'B';
else if (average >= 70) grade = 'C';
else if (average >= 60) grade = 'D';
else grade = 'F';
// 결과 출력
System.out.println();
System.out.println("╔═══════════════════════════════════╗");
System.out.printf("║ %-33s║%n", "성적표");
System.out.println("╠═══════════════════════════════════╣");
System.out.printf("║ 이름: %-27s║%n", name);
System.out.printf("║ 학번: %-27s║%n", studentId);
System.out.printf("║ 나이: %-27s║%n", age + "세");
System.out.printf("║ 학과: %-27s║%n", major);
System.out.println("╠═══════════════════════════════════╣");
System.out.printf("║ 국어: %3d점%24s║%n", korean, "");
System.out.printf("║ 수학: %3d점%24s║%n", math, "");
System.out.printf("║ 영어: %3d점%24s║%n", english, "");
System.out.println("╠═══════════════════════════════════╣");
System.out.printf("║ 총점: %3d점 평균: %5.2f점 학점: %c ║%n",
total, average, grade);
System.out.println("╚═══════════════════════════════════╝");
} catch (NumberFormatException e) {
System.err.println("점수는 숫자로 입력해주세요!");
}
}
}
입력/출력 예시:
╔════════════════════════════╗
║ 학생 정보 입력 시스템 ║
╚════════════════════════════╝
이름: 김자바
학번: 20240001
나이: 20
학과: 컴퓨터공학과
과목 점수를 입력하세요 (0~100):
국어: 92
수학: 85
영어: 88
╔═══════════════════════════════════╗
║ 성적표 ║
╠═══════════════════════════════════╣
║ 이름: 김자바 ║
║ 학번: 20240001 ║
║ 나이: 20세 ║
║ 학과: 컴퓨터공학과 ║
╠═══════════════════════════════════╣
║ 국어: 92점 ║
║ 수학: 85점 ║
║ 영어: 88점 ║
╠═══════════════════════════════════╣
║ 총점: 265점 평균: 88.33점 학점: B ║
╚═══════════════════════════════════╝
11. 입출력 형식 정리 치트시트
public class CheatSheet {
public static void main(String[] args) {
// 자주 쓰는 printf 패턴
int n = 42;
double d = 3.14159;
String s = "Java";
// 정수 출력
System.out.printf("%d%n", n); // 42
System.out.printf("%5d%n", n); // [ 42] 오른쪽 정렬
System.out.printf("%-5d%n", n); // [42 ] 왼쪽 정렬
System.out.printf("%05d%n", n); // [00042] 0 패딩
System.out.printf("%,d%n", 1000000); // 1,000,000 천단위 구분
// 실수 출력
System.out.printf("%.2f%n", d); // 3.14
System.out.printf("%8.2f%n", d); // [ 3.14]
System.out.printf("%-8.2f%n", d); // [3.14 ]
System.out.printf("%e%n", d); // 3.141590e+00
// 문자열 출력
System.out.printf("%s%n", s); // Java
System.out.printf("%10s%n", s); // [ Java]
System.out.printf("%-10s%n", s); // [Java ]
System.out.printf("%.3s%n", s); // Jav (최대 3자리)
}
}
정리
println(): 출력 + 자동 줄바꿈print(): 출력 (줄바꿈 없음)printf(): 형식 지정 출력 (%d,%s,%f,%.2f,%10s등)- Scanner: 키보드 입력 읽기,
next(),nextLine(),nextInt(),nextDouble() - nextInt() 후 nextLine() 주의: 버퍼에 남은
\n소비 필요 - BufferedReader: 빠른 입력,
readLine()+parseInt()조합 - try-with-resources: Scanner/BufferedReader 자동 close
- System.err: 에러 메시지 출력용 스트림