너굴 개발 일지

[Java] BufferedReader, BufferedWriter 본문

Java

[Java] BufferedReader, BufferedWriter

너굴냥 2021. 9. 7. 14:44

Java에서는 Scanner와 System.out.println 대신 BufferedReader와 BufferedWriter를 사용할 수 있습니다.

BufferedReader, BufferedWriter는 버퍼를 이용해 입출력 효율을 높일 수 있도록 해주는 역할을 합니다.

버퍼를 이용하면 입출력의 효율이 비교할 수 없을 정도로 좋아지기 때문에 사용하는 것이 좋습니다.

 

BufferedReader의 readLine()을 사용하면 데이터를 라인단위로 읽을 수 있고 BufferedWriter는 newLine()이라는 줄바꿈을 해주는 메소드가 있습니다.

 

[BufferedReader 사용법]

BufferedReader bf = new BufferedReader(new InputStreamReader(System.in)); //선언
String s = bf.readLine(); //String
int i = Integer.parseInt(bf.readLine()); //Int

여기서 readLine() 메소드 사용시 리턴값은 항상 String이기에 다른 타입으로 입력받으려면 형변환을 해야 합니다.

또한 예외처리를 해줘야 하기에 try&catch를 사용하거나 throws IOException 를 통하여 작업해야 합니다.

 

[readLine() 데이터 가공법]

StringTokenizer st = new StringTokenizer(s); //StringTokenizer인자값에 입력 문자열 넣음
int a = Integer.parseInt(st.nextToken()); //첫번째 호출
int b = Integer.parseInt(st.nextToken()); //두번째 호출

// 참고
int cnt = st.countTokens(); 	// 전체가 아닌 현재 남아있는 token 수 반환


String array[] = s.split(" "); //공백마다 데이터 끊어서 배열에 넣음

Read한 데이터는 Line단위로만 나눠지기에 공백단위로 데이터를 가공하려면 따로 작업을 해주어야하는데, 위의 두가지 방법이 대표적입니다.

첫번째 방법으로는 StringTokenizer에 nextToken()함수를 쓰면 readLine()을 통해 입력받은 값을 공백단위로 구분하여 순서대로 호출할 수 있습니다.

두번째 방법으로는 String.split()함수를 활용하여 배열에 공백단위로 끊어서 데이터를 넣고 사용하는 방식입니다.

 

참고로 split은 항상 배열을 반환하기에 만약 String s가 문자열이 아닌 공백이어도 배열을 반환하게 됩니다.

 

 

[BufferedWriter 사용법]

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

BufferedWriter의 경우 버퍼를 잡아 놓았기 때문에 반드시 flush(), close()를 호출하여 마지막 처리를 해야 합니다.

그리고 bw.write()에는 System.out.println();과 같이 자동개행기능이 없기때문에 개행을 해주어야할 경우에는 \n를 통해 따로 처리해주거나 bw.write() 호출 후 bw.newLine()을 호출하여 줄바꿈을 해주면 됩니다.

 

[관련 예시]

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

 

15552번: 빠른 A+B

첫 줄에 테스트케이스의 개수 T가 주어진다. T는 최대 1,000,000이다. 다음 T줄에는 각각 두 정수 A와 B가 주어진다. A와 B는 1 이상, 1,000 이하이다.

www.acmicpc.net

* 입력

첫 줄에 테스트케이스의 개수 T가 주어진다. T는 최대 1,000,000이다. 다음 T줄에는 각각 두 정수 A와 B가 주어진다. A와 B는 1 이상, 1,000 이하이다.

 

* 출력

각 테스트케이스마다 A+B를 한 줄에 하나씩 순서대로 출력한다.

package hellojpa;

import java.io.*;
import java.util.StringTokenizer;

public class Main {

    public static void main(String[] args) throws IOException {

        BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
        int T = Integer.parseInt(bf.readLine());


        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

        for(int i=1;i<=T;i++){
            StringTokenizer st = new StringTokenizer(bf.readLine());

            int a = Integer.parseInt(st.nextToken());
            int b = Integer.parseInt(st.nextToken());
            bw.write(Integer.toString(a+b));
            bw.newLine();
        }
        bw.flush();
        bw.close();
    }
}

위 코드를 실행하여 문제와 같이 입력한 결과

 


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

 

1152번: 단어의 개수

첫 줄에 영어 대소문자와 띄어쓰기로 이루어진 문자열이 주어진다. 이 문자열의 길이는 1,000,000을 넘지 않는다. 단어는 띄어쓰기 한 개로 구분되며, 공백이 연속해서 나오는 경우는 없다. 또한

www.acmicpc.net

 

* 문제

영어 대소문자와 띄어쓰기만으로 이루어진 문자열이 주어진다. 이 문자열에는 몇 개의 단어가 있을까? 이를 구하는 프로그램을 작성하시오. 단, 한 단어가 여러 번 등장하면 등장한 횟수만큼 모두 세어야 한다.

 

* 입력

첫 줄에 영어 대소문자와 띄어쓰기로 이루어진 문자열이 주어진다. 이 문자열의 길이는 1,000,000을 넘지 않는다. 단어는 띄어쓰기 한 개로 구분되며, 공백이 연속해서 나오는 경우는 없다. 또한 문자열의 앞과 뒤에는 공백이 있을 수도 있다.

 

* 출력

첫째 줄에 단어의 개수를 출력한다.

 

 

[문제 해결]

- 틀린 답안

package hellojpa;

import java.io.*;
import java.util.*;

public class Main {

    public static void main(String[] args) throws IOException {
        Scanner s = new Scanner(System.in);
        String str = s.nextLine();
        String changed = str.trim();
        String[] Arr = changed.split(" ");
        // 공백만 입력받을 경우 단어의 개수는 0이지만, split에서는 반드시 배열 반환
        // 따라서 1로 출력됨
        System.out.println(Arr.length);
    }

}

처음엔 이렇게 작성후 제출하였는데 틀렸다고 해서 당황했었습니다... 분명 예제를 입력하면 맞게 출력이 되는데 틀렸는지 고민했었는데 틀린 원인은 이러했습니다...!

split() 메소드의 경우 공백의 문자열이어도 항상 배열을 반환하기에 실질적으론 단어의 개수가 0개지만 1로 출력이 되는 것이었습니다.

따라서 BufferedReader, BufferedWriter, StringToken을 이용하고 앞뒤 공백 제거 후 conutToken() 메소드를 이용하여 남아있는 token 개수를 반환하여 풀었습니다.

 

- 맞는 답안

package hellojpa;

import java.io.*;
import java.util.*;

public class Main {

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String str = br.readLine();
        String noSpace = str.trim();
        StringTokenizer st = new StringTokenizer(noSpace);
        int cnt = st.countTokens();

        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        bw.write(Integer.toString(cnt));
        bw.flush();
        bw.close();
    }
}