본문 바로가기

WINK-(Web & App)/JAVA 스터디

[2024-2 Java 스터디] 김지수 #5주차

반응형

콘솔 입출력

콘솔이란? 콘솔(console) 은 환경에 따라 변경될 수 있다. 인텔리제이에서 실행했다면 인텔리제이의 콘솔 창이 콘솔이 될 것이고 윈도우 명령 창에서 프로그램을 실행했다면 명령 창이 콘솔이 된다. 즉, 콘솔은 사용자의 입력을 받거나 사용자에게 문자열을 출력해 주는 역할을 하는 것을 통칭하는 말이다.

인텔리제이 콘솔 창

 

콘솔 입력

생년월일을 입력하라는 질문

2002/01/01

 

자바에서 사용자가 입력한 문자열을 얻기 위해서는 다음과 같이 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) 해서 사용해야 한다.

 

InputStream

InputStream은 바이트(byte) 단위의 데이터를 읽어 들일 때 사용하는 내장 클래스이다. 앞서 만든 프로그램을 실행해 이번엔 abc를 연속해서 입력한 후 Enter 키를 눌러 보자. a를 입력했을 때와 마찬가지로 97이 출력된다.

97

이렇게 사용자가 전달한 1byte의 데이터 또는 3byte의 데이터를 입력 스트림이라고 한다. 스트림(stream) 은 byte의 흐름을 추상화한 개념이며 데이터를 byte 단위로 다룬다.

 

그렇다면 사용자가 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);
    }
}
abc (입력 + 엔터)
97 (출력)
98 (출력)
99 (출력)

read 메서드를 3번 실행하도록 수정하고 프로그램을 다시 실행해 보자. 이번에는 abc를 입력하면 총 3byte를 읽어 들여 a, b, c 각각의 아스키코드값을 출력한다.

 

InputStreamReader

우리가 입력한 문잣값을 그대로 출력해 볼 수는 없을까? byte 대신 문자로 입력 스트림을 읽으려면 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 문이 하나 더 추가되었다.

 

프로그램을 실행해 abc를 입력한 후 Enter 키를 눌러 사용자 입력을 전달해 보자. 그럼 다음과 같이 문자열 abc가 한꺼번에 출력되는 것을 확인할 수 있다.

abc (입력)
abc (출력)

 

BufferedReader

앞서 살펴본 예제에서는 3byte만 읽도록 고정되어 있었다. 길이에 상관없이 사용자가 입력한 값을 모두 받아들일 수는 없을까?

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"를 입력한 후 Enter 키를 누르면 문자열 "Hello World"가 그대로 출력되는 것을 확인할 수 있다.

Hello World (입력)
Hello World (출력)

자바 입문자는 스트림을 처음 배울 때 대부분 혼란스러워 한다. 감싸고, 감싸고, 또 감싸니 도대체 내부적으로 어떻게 돌아가는지 헷갈릴 수밖에 없다. 다음과 같이 기억해 보자.

  • InputStream: byte를 읽는다.
  • InputStreamReader: character(문자)를 읽는다.
  • BufferedReader: String(문자열)을 읽는다.

Scanner

J2SE 5.0부터 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해야 한다. Scanner 클래스는 생성자의 입력으로 System.in 객체가 필요한데, 이는 콘솔 입력인 InputStream이 필요하다는 의미이다.

  • next: 토큰을 읽어 들일 수 있다.
  • nextLine: 라인을 읽어 들일 수 있다.
  • nextInt: 정수를 읽어 들일 수 있다.

콘솔 출력

우리는 System.out.println 메서드를 계속해서 사용해 왔다. System.out은 PrintStream 클래스의 객체이다. PrintStream은 콘솔에 값을 출력할 때 사용되는 클래스이다. 보통 System.out.println은 콘솔에 문자열을 출력할 때나 디버깅할 때 많이 사용한다. System.err도 있는데 System.out과 동일한 역할을 한다. 다만 System.err는 오류 메시지를 출력할 때 사용한다.

 


파일 입출력

파일 쓰기

import java.io.FileOutputStream;
import java.io.IOException;

public class Sample {
    public static void main(String[] args) throws IOException {
        FileOutputStream output = new FileOutputStream("c:/out.txt");
        output.close();
    }
}

이 예제를 실행하면 c:/ 디렉터리 바로 밑에 새로운 파일인 out.txt가 생성되는 것을 확인할 수 있다.

 

FileOutputStream 클래스를 사용하면 이와 같이 파일을 생성할 수 있다. FileOutputStream 클래스는 객체를 생성할 때 생성자의 입력으로 파일명을 넘겨주어야 한다. 이 예제에서는 경로를 포함하여 c:/out.txt라는 파일명을 생성자의 입력으로 전달하였다.

 

output.close()라는 문장이 있는데 이것은 사용한 파일 객체를 닫아 주기 위한 코드이다. 사실 이 문장은 생략해도 된다. 왜냐하면 자바 프로그램을 종료할 때 사용한 파일 객체를 자동으로 닫아 주기 때문이다. 그럼에도 불구하고 직접 사용한 파일은 항상 닫아 주는 것이 좋다. 사용했던 파일을 닫지 않고 다시 사용하려고 할 경우에는 오류가 발생하기 때문이다.

 

FileOutputStream

import java.io.FileOutputStream;
import java.io.IOException;

public class Sample {
    public static void main(String[] args) throws IOException {
        FileOutputStream output = new FileOutputStream("c:/out.txt");
        for(int i=1; i<11; i++) {
            String data = i+" 번째 줄입니다.\r\n";
            output.write(data.getBytes());
        }
        output.close();
    }
}

 

InputStream과 마찬가지로 OutputStream 역시 byte 단위로 데이터를 처리하는 클래스이다. 여기서 사용한 FileOutputStream은 OutputStream 클래스를 상속받아 만든 클래스이므로 역시 byte 단위로 데이터를 처리한다.

 

FileWriter

import java.io.FileWriter;
import java.io.IOException;

public class Sample {
    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("c:/out.txt");
        for(int i=1; i<11; i++) {
            String data = i+" 번째 줄입니다.\r\n";
            fw.write(data);
        }
        fw.close();
    }
}

FileOutputStream 대신에 FileWriter를 이용하면 byte 배열 대신 문자열을 사용할 수 있어 편리하다.

 

PrintWriter

FileWriter를 사용하더라도 \r\n을 문자열 뒤에 덧붙여야 해 번거롭다. 이런 불편함을 해소하려면 FileWriter 대신 PrintWriter를 사용하면 된다. PrintWriter를 이용하면 \r\n을 덧붙이는 대신 println이라는 메서드를 사용할 수 있다. 

import java.io.IOException;
import java.io.PrintWriter;

public class Sample {
    public static void main(String[] args) throws IOException {
        PrintWriter pw = new PrintWriter("c:/out.txt");
        for(int i=1; i<11; i++) {
            String data = i+" 번째 줄입니다.";
            pw.println(data);
        }
        pw.close();
    }
}

 

PrintWriter를 사용한 프로그램을 실행한 후, out.txt 파일에 어떤 내용이 저장되었는지 확인해 보자. System.out을 사용한 프로그램을 실행했을 때 콘솔에 출력될 내용이 파일에 고스란히 들어가 있는 것을 확인할 수 있다.

 

PrintWriter를 사용한 파일 출력 화면

System.out을 사용한 콘솔 출력 화면

 

파일에 내용 추가하기

프로그램을 만들다 보면 파일에 내용을 입력한 후 새로운 내용을 추가할 수도 있다. 이런 경우에는 이미 작성된 파일을 추가 모드로 열어서 추가할 내용을 작성하면 된다.


import java.io.FileWriter;
import java.io.IOException;

public class Sample {
    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("c:/out.txt");
        for(int i=1; i<11; i++) {
            String data = i+" 번째 줄입니다.\r\n";
            fw.write(data);
        }
        fw.close();

        FileWriter fw2 = new FileWriter("c:/out.txt", true);  // 파일을 추가 모드로 연다.
        for(int i=11; i<21; i++) {
            String data = i+" 번째 줄입니다.\r\n";
            fw2.write(data);
        }
        fw2.close();
    }
}

fw2 객체는 FileWriter("c:/out.txt", true)에서 알 수 있듯 두 번째 파라미터를 추가로 전달하여 생성했다.

 

파일 읽기

import java.io.FileInputStream;
import java.io.IOException;

public class Sample {
    public static void main(String[] args) throws IOException {
        byte[] b = new byte[1024];
        FileInputStream input = new FileInputStream("c:/out.txt");
        input.read(b);
        System.out.println(new String(b));  // byte 배열을 문자열로 변경하여 출력
        input.close();
    }
}

다만 byte 배열을 이용하여 파일을 읽어야 하기 때문에 읽어야 하는 데이터 길이를 모를 경우에는 좀 불편한 방법이다. 여기서는 1024byte를 읽도록 코딩했다.

 

파일을 한 줄(라인) 단위로 읽어 들일 수 있다면 훨씬 편리할 것이다. FileInputStream 대신 FileReader와 BufferedReader의 조합을 사용하면 한 줄 단위로 파일을 읽을 수 있다.

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class Sample {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("c:/out.txt"));
        while(true) {
            String line = br.readLine();
            if (line==null) break;  // 더 이상 읽을 라인이 없을 경우 while 문을 빠져나간다.
            System.out.println(line);
        }
        br.close();
    }
}

BufferedReader의 readLine 메서드는 더 이상 읽을 라인이 없을 경우 null을 리턴한다.


객관식 문제

  1. Java에서 표준 출력에 사용되는 클래스는 무엇인가요? (정답:b)
    • A) Scanner
    • B) System
    • C) BufferedReader
    • D) InputStream
  2. System.out.println()에서 println의 역할은 무엇인가요? (정답:b)
    • A) 파일을 읽는다.
    • B) 화면에 내용을 출력한다.
    • C) 텍스트를 입력받는다.
    • D) 문자열을 객체로 변환한다.
  3. Java의 입출력 클래스 중 버퍼를 사용하여 효율적인 입출력을 제공하는 클래스는 무엇인가요? (정답:b)
    • A) File
    • B) BufferedReader
    • C) Scanner
    • D) System
  4. 파일에 텍스트를 쓰기 위해 가장 많이 사용되는 클래스는? (정답:b)
    • A) FileReader
    • B) FileWriter
    • C) BufferedReader
    • D) Scanner
  5. Java의 Scanner 클래스는 어떤 패키지에 속해 있나요? (정답:b)
    • A) java.io
    • B) java.util
    • C) java.nio
    • D) java.lang
  6. 다음 중 System.out.println()에서 out의 역할은 무엇인가요? (정답:b)
    • A) 입력을 받아오는 스트림
    • B) 표준 출력 스트림
    • C) 파일을 읽는 스트림
    • D) 네트워크 통신 스트림
  7. 파일에서 데이터를 읽어오는 대표적인 클래스는 무엇인가요? (정답:a)
    • A) FileReader
    • B) FileWriter
    • C) Scanner
    • D) PrintStream
  8. Java의 BufferedWriter 클래스는 어떤 기능을 제공하나요? (정답:d)
    • A) 파일을 삭제한다.
    • B) 파일을 읽는다.
    • C) 파일을 압축한다.
    • D) 텍스트를 버퍼에 쓰고 출력 성능을 향상시킨다.
  9. InputStream 클래스의 역할은 무엇인가요? (정답:b)
    • A) 파일을 생성한다.
    • B) 데이터를 입력받는다.
    • C) 데이터를 네트워크로 전송한다.
    • D) 데이터를 파일에 쓴다.
  10. FileReader와 FileWriter의 차이점은 무엇인가요? (정답:b)
    • A) FileReader는 데이터를 파일에 쓰고, FileWriter는 파일을 읽는다.
    • B) FileReader는 파일을 읽고, FileWriter는 데이터를 파일에 쓴다.
    • C) FileReader는 문자열만 처리하고, FileWriter는 숫자만 처리한다.
    • D) 두 클래스는 동일한 기능을 한다.

코딩테스트 문제

문제1

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        long A = sc.nextLong();
        long B = sc.nextLong();
        sc.close();
        
        System.out.println(lcm(A, B));
    }

    public static long lcm(long a, long b) {
        long max = Math.max(a, b);
        for (long i = max; i <= a * b; i += max) {
            if (i % a == 0 && i % b == 0) {
                return i;
            }
        }
        return a * b;
    }
}

 

문제2

import java.util.Arrays;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        
        int N = sc.nextInt();
        int K = sc.nextInt();
        int[] scores = new int[N];
        
        for (int i = 0; i < N; i++) {
            scores[i] = sc.nextInt();
        }
        
        sc.close();
        
        Arrays.sort(scores); 
        System.out.println(scores[N - K]); 
    }
}
반응형