개발/Java

Java I/O

Debin 2022. 5. 27.
반응형

백기선님이 과거에 진행했던 Java 스터디 13주차 스터디 입니다.

Stream, Buffer

Stream이란?

자바에서 입출력을 수행하려면, 즉 어느 한쪽에서 다른 쪽으로 데이터를 전달하려면,

두 대상을 연결하고 데이터를 전송할 수 있는 무언가가  필요한데 이것을 스트림이라고 정의한다

스트림이란 데이터를 운반하는데 사용되는 연결통로다

스트림은 단방향통시만 가능하기 때문에 하나의 스트림으로 입력과 출력을 동시에 처리할 수 없다.

그래서 입력과 출력을 동시에 수행하려면 입력을 위한 입력스트림과 출력을 위한 출력스트림, 총 2개의 스트림이 필요하다.

스트림은 먼저 보낸 데이터를 먼저 받게 되어 있으며 중간에 건너뜀 없이 연속적으로 데이터를 주고받는다.

큐와 같은 FIFO구조로 되어 있다고 생각하면 된다.

Buffer란?

위키에서 버퍼의 정의는 아래와 같다.

컴퓨팅에서, 버퍼(buffer)는 데이터를 한 곳에서 다른 한 곳으로 전송하는 동안 일시적으로 그 데이터를 보관하는 메모리의 영역이다. 버퍼링(buffering)이란 버퍼를 활용하는 방식 또는 버퍼를 채우는 동작을 말한다. 다른 말로 '큐(Queue)'라고도 표현한다.
버퍼는 컴퓨터 안의 프로세스 사이에서 데이터를 이동시킬 때 사용된다. 보통 데이터는 키보드와 같은 입력 장치로부터 받거나 프린터와 같은 출력 장치로 내보낼 때 버퍼 안에 저장된다. 이는 전자 통신의 버퍼와 비유할 수 있다. 버퍼는 하드웨어나 소프트웨어에 추가될 수 있지만 버퍼는 상당수가 소프트웨어에 추가된다. 버퍼는 보통 속도가 계속 바뀔 수 있으므로 데이터 수신, 처리 속도에 차이가 있다. (예: 프린터 스풀러)
버퍼는 네트워크 상에서 자료를 주고 받을 때나 스피커에 소리를 재생할 때, 또는 디스크 드라이브와 같은 하드웨어의 입출력을 결합하는 데에 자주 이용된다. 버퍼는 또한 순서대로 데이터를 출력하는 FIFO 방식에서 보통 사용된다.

즉 Buffer, 버퍼란 임시 메모리 공간이다.

버퍼를 사용하는 스트림은, 입력을 받은 데이터를 버퍼에 모았다가 자바 애플리케이션에 전달할 수 있다.

버퍼란

버퍼를 사용하면 아래와 같은 장점을 얻을 수 있다.

  1. 문자를 하나씩 전달하는 것이 아닌 묶어서 한 번에 전달하므로, 전송 시간이 적게 걸려 성능이 향상된다.
  2. 사용자가 문자를 잘못 입력했을 경우 수정을 할 수가 있다.

하지만 입력 작업에 버퍼를 사용하는 것이 반드시 좋은 것만은 아니다.

빠른 반응이 요구되는 게임과 같은 프로그램에서는 키를 누르는 즉시 바로 전달되어야만 한다.

이렇게 버퍼를 사용하는 입력과 버퍼를 사용하지 않는 입력은 서로 다른 용도로 사용된다.

따라서 자신의 목적에 맞게 버퍼의 사용 여부를 판단해야 한다.

InputStream과 OutputStream

InputStream 과 OutputStream은 바이트 스트림의 조상이다.

다양한 바이트 기반 스트림은 각각 읽고 쓰는데 필요한 메서드를  InputStream과 OutputStream 추상 메서드를 오버라이딩해자신에 맞게 구현해 놓았다.

InputStream과 OutputStream에 정의된 읽기와 쓰기를 수행하는 메서드는 다음과 같다.

InputStream OutputStream
abstract int read() abstract void write(int b)
int read(byte[] b) void write(byte[] b)
int read(byte[] b, int off, int len) void write(byte[] b, int off, int len)

InputStream의 read()와 OutputStream의 write(int b)는 입출력의 대상에 따라 읽고 쓰는 방법이 다를 것이기 때문에

각 상황에 알맞게 구현하라는 의미에서 추상메서드로 정의되어 있다.

Byte스트림

Byte스트림은 바이트단위로 데이터를 전송하며 입출력 대상에 따라 다음과 같은 입출력스트림이 있다.

입력스트림 출력스트림 입출력 대상의 종류
FileInputStream FileOutputStream 파일
ByteArrayInputStream ByteArrayOutputStream 메모리 (byte 배열)
PipedInputStream PipedOutputStream 프로세스(프로세스간의 통신)
AudioInputStream AudioOutputStream 오디오장치

위 표와 같이 여러 종류의 입출력 스트림이 있으며, 어떠한 대상에 대해 작업을 할것인지에 따라 해당 스트림을 선택해서 사용한다.

예를 들어 파일의 내용을 읽고자 하는 경우 FileInputSTream을 사용하면 된다.

Character 스트림

지금까지는 바이트기반의 스트림에 대해 알아보았다.

바이트기반은 입출력의 단위가 1byte라는 것이다.

C언어와 다르게 Java는 char형이 1byte가 아니라 2byte다. 때문에 바이트기반의 스트림으로 char형을 처리하는데 어려움이 있다.

이 점을 보완하기 위해서 문자기반의 스트림이 제공된다.

  • InputStream -> Reader
  • OutputStream -> Writer
바이트기반 스르틺 문자기반 스트림
FileInputStream
FileOutputStream
FileReader
FilerWriter
ByteArrayInputStream
ByteARrayOutStream
CharArrayReader
CharArrayWriter
PipedInputStream
PipedOutputStream
PipedReader
PipedWriter
StringBufferInputStream (deprecated)
StringBufferOutputStream (deprecated)
StringReader
StringWriter

참고로 StringBufferInputStream, StringBufferOutputStream는 각각 StringReader과 StringWriter로 대체되었다.

 

이제 InputStream, Reader 각각의 메서드에 대한 차이를 확인해보자. 물론 OutputStream과 Writer 메서드의 차이도 확인할 것이다.

InputStream Reader
abstract int read() int read()
int read(byte[] b) int read (char[] cbuf)
int read(byte[] b, int off, int len) abstract int read(char[] cbuf, int off, int len)

 

OutputStream Writer
abstract void write(int b)
void write (byte[] b)
void write(byte[] b, int off, int len)
void write(int c)
void write(char[] cbuf)
abstract void write (char[] cbuf, int off, int len)
void write(String str)
void write(String str, int off, int len)

공통적으로 byte배열 대신 char배열을 사용한다는 것과 추상메서드가 달라졌다.

InputStreamReader과 OutputStreamWriter

InputStreamReader/OutputStreamWriter는 이름에서 알 수 있는 것과 같이

바이트기반 스트림을 문자기반 스트림으로 연결시켜주는 역할을 한다.

그리고 바이트기반스트림의 데이터를 지정된 인코딩의 문자데이터로 변환하는 작업을 수행한다.

표준 스트림 (System.in, System.out, System.err)

표춘입출력은 콘솔(console, 도스창)을 통한 데이터 입력과 콘솔로의 데이터 출력을 의미한다.

자바에서는 표준 입출력을 위해 3가지 입출력 스트림, System.in, System.out. System.err을 제공한다.

이들은 자바 애플리케이션의 실행과 동시에 사용할 수 있게 자동적으로 생성되기 때문에

개발자가 별도로 스트림을 생성하는 코드를 작성하지 않고도 사용이 가능하다.

  • System.in: 콘솔로부터 데이터를 입력받는데 사용
  • System.out: 콘솔로 데이터를 출력하는데 사용
  • System.err:콘솔로 데이터를 출력하는데 사용

System 클래스를 확인해보면 in, out, err은 System클래스에 선언된 클래스변수(static 변수)다.

선언부분을 봐서는 out, err, in의 타입은 InputStream과 PrintStream이지만 실제로는 버퍼를 사용하는

BufferedInputStream과 BufferedOutputStream의 인스턴스를 사용한다.

 

public final class Systen{
    public final static InputStream in = nullInputStream();
    public final static PrintStream out = nullPrintStream();
    public final static PrintStream err = nullPrintStream();
}

콘솔입력은 버퍼를 가지고 있기 때문에 Backspace키를 이용해서 편집이 가능하며 한 번에 버퍼의 크기만큼 입력이 가능하다.

그래서 엔터키나 입력의 끝을 알리는 ^z를 누르기 전까지는 아직 데이터가 입력중인 것으로 간주되어

커서가 입력을 계속 기다리는 상태(블락킹 상태)에 머무르게 된다.

콘솔에 데이터를 입력하고 엔터키를 누르면 입력대기상태에서 벗어나 입력된 데이터를 읽기 시작하고

입력된 데이터를 모두 읽으면 다시 입력대기 상태가 된다.

파읽 읽고 쓰기

성능 향상을 위해 버퍼 입출력을 사용했다. 이런 성능 향상을 도와주는 스트림을 보조 스트림이라고 한다.

hello.txt 파일을 읽고 result.txt 파일에 hello.txt 파일의  내용을 작성한다.

package happysubin.javapractice.javastudy.week13;

import java.io.*;

public class FileExample {
    public static void main(String[] args) throws IOException {
        File file = new File("/Users/ansubin/Desktop/hello.txt");
        File resultFile = new File("/Users/ansubin/Desktop/result.txt");

        BufferedReader br = new BufferedReader(new FileReader(file));
        BufferedWriter bw = new BufferedWriter(new FileWriter(resultFile));

        String str;
        while ((str = br.readLine()) != null){
           bw.write(str + "\n");
        }


        br.close();
        bw.close();

    }
}

 

이상으로 포스팅을 마칩니다. 감사합니다.

 

참고자료

자바의정석(저자: 남궁성, 입출력 I/O)

http://www.tcpschool.com/c/c_io_console

https://ko.wikipedia.org/wiki/%EB%B2%84%ED%8D%BC_(%EC%BB%B4%ED%93%A8%ED%84%B0_%EA%B3%BC%ED%95%99) 

 

반응형

댓글