본문 바로가기

Language/Java

자바 입출력과 관련 메서드에 대해 ARABOJA!

자바로 알고리즘 풀이를 하던 와중 Scanner 외에도 BufferedReader를 알게 되었고, 효율성 및 시간 복잡도를 고려하게 되었다. 

그렇다면 자바 입력과 출력은 어떤 상황에서 어떻게 써야할까?

 

우선 자바의 입출력부터 간단하게 알아보자.


 

입출력이란?

I/O란 Input과 Output의 약자로 입력과 출력, 간단히 줄여서 입출력이라고 한다. 입출력은 컴퓨터 내부 또는 외부의 장치와 프로그램 간의 데이터를 주고받는 것을 말한다.

 

자바의 모든 입출력은 스트림으로 이루어지는데, 스트림이란 데이터를 운반하는 데 사용되는 연결 통로이다. 스트림은 연속적인 데이터의 흐름을 물에 비유해 붙어진 이름인데, 흐르는 것처럼 단방향 통신만 가능하기 때문에 하나의 스트림으론 입력과 출력을 동시에 할 수 없다.

 

따라서, 입력 스트림과 출력 스트림이 각각 필요하며 데이터를 주고받을 때 큐(queue)와 같은 FIFO 구조로 되어있다고 생각하면 된다.

 

자바에는 수많은 입출력에 관한 스트림이 있지만, 그중 표준 입출력인 Scanner와 문자 기반의 보조 스트림인 BufferedReader에 대해서 자세히 알아보려고 한다.

 

입력

Scanner vs BufferedReader

// Scanner
Scanner scanner = new Scanner(System.in); 
int a = scanner.nextInt();

Scanner의 특징

Scanner은 지원해주는 메서드가 많고 쉽기 때문에 자주 사용하게 된다. 예를 들면 정수 값으로 int, short, long 소수 값으론 float,double을 구분지어 읽을 수 있으며 String값도 읽을수 있다. 하지만 버퍼 사이즈가 1024이며 많은 작업을 요구할 땐 당연히 성능상 저하가 올 수 밖에 없다.

 

Scanner에 주요 메소드

메소드
설명
String next()
다음 토큰을 문자열로 리턴
byte nextByte()
다음 토큰을 byte 타입으로 리턴
short nextShort()
다음 토큰을 short 타입으로 리턴
int nextInt()
다음 토큰을 int 타입으로 리턴
long nextLong()
다음 토큰을 long 타입으로 리턴
float nextFloat()
다음 토큰을 float 타입으로 리턴
double nextDouble()
다음 토큰을 double 타입으로 리턴
String nextLine()
' \n '을 포함하는 한 라인을 읽고 ' \n '을 버린 나머지만 리턴
void close()
Scanner의 사용 종료
boolean hasNext()
현재 입력된 토큰이 있으면 true, 아니면 새로운 입력이 들어올 때까지 무한정 기다려서, 새로운 입력이 들어오면 그 때 true 리턴. ctrl + z 키가 입력되면 입력 끝이므로 false 리턴

 

BufferedReader의 특징

BufferedReader은 크기는 8KB로 즉, 긴 문자열이 포함된 파일을 읽을 시에는 BufferedReader을 추천하지만 내용이 짧을 경우 Scanner을 사용하는 것을 추천한다. 즉 여러 스레드 간에 Scanner은 공유할 수 없지만 BufferReader개체는 공유할 수 있다. 동기화를 사용하는 BufferedReader의 경우 싱글 스레드를 사용하는 Scanner보다 약간 느리다. 하지만 Scanner의 경우 정규식을 사용하여 값을 받으므로 이러한 속도 차이는 보상을 넘어 BufferedReader가 더 빠르게 문자열을 읽을 수 있게 된다. 하지만, BufferedReader은 오직 문자열 값만을 읽기 때문에 readLine() 함수만을 사용한다. 따라서 경계로 입력값을 Enter로만 받기 때문에 가공하는 작업이 필요할 수 있다.

// 문자기반 보조스트림 사용하기 위해 Import 해야 하는 명령어
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

BufferedReader 주요 메서드 및 특징

// BufferedReader
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in)); //버퍼리더 선언

String string = bufferedReader.readLine(); // 라인 단위로 입력받기
StringTokenizer stringTokenizer = new StringTokenizer(string);
// 토크나이져를 통해 파싱을 한다 지금은 띄어쓰기 단위로 잘라준다
// 혹은, String arr[] = str.split(" "); 으로 끊어서 받을 수도 있음
int a = Integer.parseInt(stringTokenizer.nextToken());
// readLine()시 리턴값이 String이므로 형변환이 필요함

※ 주의할 점 

    • readLine() 시 리턴 값을 String으로 고정되기에 String이 아닌 다른 타입으로 입력을 받으려면 형 변환을 꼭 해주어야 한다는 점이다.
    • 예외처리를 꼭 해주어야한다는 점이다. readLine을 할 때마다 try & catch를 활용하여 예외처리를 해주어도 되지만 대개 throws IOException을 통하여 작업한다. (import java.io.IOException; or main 클래스 옆에 throws IOException작성)
    • readLine() 함수로 경계를 구별하기 위해 두 가지 방법을 쓰는데, StringTokenizer에 nextToken() 함수를 쓰면 readLine()을 통해 입력받은 값을 공백 단위로 구분하여 순서대로 호출할 수 있다. 혹은 String.split()함수를 활용하여 배열에 공백단위로 끊어서 데이터를 넣고 사용하는 방식을 사용할 수 있다.

출력

Bufferedwriter

BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));   //할당된 버퍼에 값 넣어주기
String s = "abcdefg";   //출력할 문자열
bw.write(s+"\n");   //버퍼에 있는 값 전부 출력
bw.flush();   //남아있는 데이터를 모두 출력시킴
bw.close();   //스트림을 닫음

BufferedWriter의 경우 버퍼를 잡아놓았기 때문에 해제해주는 작업이 필요하다. flush() / close() 함수를 호출함으로써 처리해줄 수 있다. 또한 write()에는 println()과 같이 자동 개행 기능이 없기 때문에 개행이 필요한 경우 \n을 통해 해 주면 된다.

System.out.println()는 표준 출력에 평소에 많이 다뤄봤으므로 넘어가겠다.

 

StringTokenizer 메서드

BufferedReader로 읽을 때 StringTokenizer에 필요한 메서드를 알아보자.

메소드
설명
hasMoreTokens()
남아있는 토큰이 있으면 true 리턴, 아니면 false 리턴 (boolean)
nextToken()
객체에서 다음 토큰을 반환 (String)
nextElement()
nextToken 메서드와 동일하지만 문자열이 아닌 객체를 리턴 (Object)
countTokens()
총 토큰의 개수를 리턴 (int)

이를 토대로 백준 문제를 풀어본 후 확실히 이해해보자.

백준 15552번 빠른 A+B

https://www.acmicpc.net/problem/15552

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.StringTokenizer;
public class Main {
    public static void main(String[] args) throws IOException 	{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringTokenizer st;
        int T = Integer.parseInt(br.readLine());

        for(int i =0; i<T; i++)
        {
            st = new StringTokenizer(br.readLine());
            bw.write((Integer.parseInt(st.nextToken()) + Integer.parseInt(st.nextToken())) + "\n");
        }
        bw.close();
    }
}

 

출처: https://snupi.tistory.com/48 [SNUPI]

출처: https://velog.io/@mohai2618/Java-%EC%9E%85%EC%B6%9C%EB%A0%A5-%EB%A9%94%EC%84%9C%EB%93%9C-%EC%A0%95%EB%A6%AC

출처: https://friends-aihaja.tistory.com/entry/1-BufferReader-VS-Scanner-%EC%B0%A8%EC%9D%B4%EC%A0%90

출처: https://m.blog.naver.com/ka28/221850826909

'Language > Java' 카테고리의 다른 글

자바를 Job Uh!! - [열거 타입]  (0) 2022.01.04
자바를 Job Uh!! - [다중 배열]  (0) 2022.01.03
자바를 Job Uh!! - [배열]  (0) 2022.01.03