Computer Engineering/Java & Spring

자바의 입력을 담당하는 BufferedReader와 StringBuilder

soohey 2022. 8. 24. 21:09

자바 초짜로써.. Scanner밖에 모르다가 BufferdReader의 존재를 알게되어 정리해보는 글. 

 

버퍼의 입출력

BufferdReader는 Scanner와 유사하며 BufferedWriter는 출력과 유사하다.

다른 점이 있다면 입력된 데이터 버퍼를 거쳐 전달이 되므로 데이터 처리 효율성이 높다.

버퍼를 거치면 한단계가 늘어나는건데 왜 빠를까?

하드디스크는 원래 속도가 엄청 느리기에 외부 입력장치를 통한 입출력은 상당히 시간이 오래 걸린다.

그래서 키보드가 눌릴때마다 문자를 이동시키는것보다는 중간에 메모리버퍼에 저장하여 한번에 이동시키는 것이 보다 효율적이다. 즉 모아뒀다가 한번에 전송한다는 뜻

많은 양의 데이터를 처리할 때 속도 측면에서 유리하다.

  • buffer : 데이터를 한 곳에서 다른 곳으로 전송하는 동안 잠시 보관하는 임시 영역. 입출력 속도 향상을 위해 사용함
  • buffer flush : 버퍼에 남아 있는 데이터를 출력시킨다. (버퍼를 비움)
  • BufferedRedaer : 버퍼를 이용한 입력
  • BufferedWriter : 버퍼를 이용한 출력

Scanner와의 차이점

보통 처음엔 Scanner를 이용해 입출력을 시도하는데, 뛰어쓰기나 개행문자를 경계로 입력 값을 인식하기에 따로 가공할 필요가 없어서 편하다.

또한 Int값을 입력받고 싶을 때 Scanner.nextInt()를 이용해 바로 입력받기가 가능하다.

반면에 BufferedReader는 Enter를 경계로 인식하고 String으로 고정되기 때문에  Int값이나 BigInter로 바꾸고 싶을 경우 따로 가공해주어야 된다.

(조금 번거롭지만.. 빠르니까 사용하는 걸로)

StringBuilder

String으로 문자열 두개를 합칠 경우 str = str1 + str2 처럼 + 연산자로 합치는 경우가 있는데, 이렇게 변경하게 되면 기존의 String 메모리의 값이 변경되는 것이 아니라 기존값을 버리고, 새로운 값을 할당하게 된다. (String의 불변성)

처음 할당한 String의 메모리 영역은 Garbage에 남아있다가 GarbageCollection에 의해 없어진다.

문자열 추가, 수정, 삭제같은 연산이 자주 일어나면 위같이 힙 메모리에 많은 Garbage가 생성되고, 이는 힙 메모리 부족으로 이어진다.

이를 해결하기 위해 StringBuilder를 사용하여 append(), delete()를 이용해 동일 객체 내에서 문자열을 바꾸도록하여 메모리 부하를 줄이도록 한다. -> 수정/삭제/추가가 빈번히 일어나면 StringBuilder 사용하는게 이득이라는 뜻

속도는 String.concat < StringBuffer.append < StringBuilder.append 순이다. (StringBuilder가 제일 빠름)

StringBuffer : 동기화를 지원하여 멀티스레드에서 안전함

StringBuilder : 동기화를 지원하지 않아 단일 스레드에서 쓰는게 유리하고 성능이 뛰어남. (코테용인걸까..)

StringBuilder sb = new StringBuilder();
sb.append("a");
sb.append("b").append(" ");
sb.append("\n");
  • append() : 뒤에 추가
  • delete(int, int) : int ~ int 인덱스 범위 삭제
  • insert(int, char[]) : int 인덱스에 문자 삽입
  • replace(start, end, s) : 일부를 string으로 치환
  • reverse() : 순서 뒤집기
  • setCharAt(index, ch) : 주어진 문자 치환
  • indexOf(s) : string이 어느 인덱스에 있는지 확인
  • subString(start, end) : 문자열 잘라오기

주의점

1) 다른 타입으로 입력받을 때는 형변환해주기

BufferedReader는 기본적으로 readLine()을 사용해서 데이터를 라인 단위로 읽는데, readLine()은 리턴값을 String으로 고정하기때문에 다른 타입으로(ex: Int) 로 입력받고 싶다면 꼭 형변환을 해주어야 한다.

BufferedRedaer br = new BufferedReader(new InputStreamReader(System.in));
int num = Integer.parseInt(br.readLine());

2) 버퍼 닫아주기

버퍼를 다 쓴 뒤에는 꼬옥 닫아주기! 버퍼를 닫아서 클린하게 유지해주자.

flush()함수를 통해 버퍼에 남아있는 데이터를 출력한 뒤, close()로 닫아준다.

BufferWirter bw = new BufferedWriter(new FileWriter(""));
bw.flush();
bw.close();

3) 데이터 가공하기

BufferedReader로 데이터를 읽어오면 개행문자, 라인 단위로 나뉘어 진다.

이를 스캐너처럼 공백단위로 가공하고자하면 StringTokenizer나 String.split()함수를 사용하자.

// StringTokenizer
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer str = new StringTokenizer(br.readLine());
int N = Integer.parseInt(st.nextToken());
int M = Integer.parseInt(st.nextToken());

// String.split()
String arr[] = str.split(" ");

4) 예외처리

IOException 예외처리를 반드시 해주어야한다. Try~Catch로 해도되고~ 나처럼 throws 해도 되고.

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

아래는 응용코드라고 할 수 있는데 백준 1247번 문제다.

총 3개의 테스트 셋이 주어지며, 첫 줄은 N으로, 둘째 줄부터 N개의 줄에 걸쳐 각 정수의 합 S의 부호를 출력한다.

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

 

1247번: 부호

총 3개의 테스트 셋이 주어진다. 각 테스트 셋의 첫째 줄에는 N(1 ≤ N ≤ 100,000)이 주어지고, 둘째 줄부터 N개의 줄에 걸쳐 각 정수가 주어진다. 주어지는 정수의 절댓값은 9223372036854775807보다 작거

www.acmicpc.net

import java.util.*;
import java.io.*;
import java.math.BigInteger;
// tip: each public class is put in its own file
public class Main
{
    // tip: arguments are passed via the field below this editor
    public static void main(String[] args) throws IOException
    {
        
        
        final BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        final int kTestcaseNum = 3;
        
        
        StringBuilder sb = new StringBuilder();
        
        for (int i=0; i<kTestcaseNum; i++){
            final int numSize = Integer.parseInt(br.readLine());
            BigInteger bi = new BigInteger("0");
            for(int j = 0; j<numSize; j++){
                bi = bi.add(new BigInteger(br.readLine()));
            }
            
            sb.append(findSign(bi.signum())).append("\n");
        }
        
        sb.setLength(sb.length() -1);
        
        System.out.print(sb);
        br.close();

    }
    
    private static char findSign(int sigNum){
        char retVal = '0';
        switch(sigNum){
            case 0:{
                break;
            }
            case -1:{
                retVal = '-';
                break;
            }
            case 1:{
                retVal = '+';
                break;
            }
        }
        return retVal;
    }
}
  • N값 만큼 int로 받기 위해 parseInt를 통해 형변환해주기
  • StringBuilder를 이용해 추가하기

 

 

 

참고

https://codemasterkimc.tistory.com/362

 

백준 1247번 부호 JAVA 구현해보기

``` 백준 1247번 부호 JAVA 구현해보기 ``` 이번 글을 통해 배워갈 내용 백준 1247번 풀이 https://www.acmicpc.net/problem/1247 1247번: 부호 총 3개의 테스트 셋이 주어진다. 각 테스트 셋의 첫째 줄에는..

codemasterkimc.tistory.com

 

https://jhnyang.tistory.com/92

 

[Java 자바 입출력] BufferedReader/BufferedWriter

[자바 입출력 함수] BufferedReader / BufferWriter BufferedReader/BufferedWriter은 이름처럼 버퍼를 이용해서 읽고 쓰는 함수입니다. 이 함수는 버퍼를 이용하기 때문에 이 함수를 이용하면 입출력의 효율이..

jhnyang.tistory.com

 

https://rlakuku-program.tistory.com/33

 

[Java] 빠른 입출력을 위한 BufferedReader, BufferedWriter, StringTokenizer, StringBuilder

BufferedReader / BufferedWriter BufferedReader와 BufferdWriter는 버퍼를 사용하여 읽기와 쓰기를 하는 함수이다. 버퍼를 사용하지 않는 입력은, 키보드의 입력이 키를 누르는 즉시 바로 프로그램에 전달된다..

rlakuku-program.tistory.com