티스토리 뷰

JAVA

[자바 스터디] 11주차 : I/O

짜비 2022. 3. 6. 07:26

스트림 (Stream) / 버퍼 (Buffer) / 채널 (Channel) 기반의 I/O

스트림

  • 데이터를 운반하는데 사용하는 연결통로
  • 단방향 통신만 가능
    • 입력, 출력을 동시에 하려면 입력스트림, 출력스트림 총 2개의 스트림이 필요
  • 먼저 보낸 데이터를 먼저 받음 (First In First Out)

버퍼

  • 스트림의 입출력 효율을 높이기 위해 사용
    • 한 바이트씩 입출력하는 것보다 한 번에 여러 바이트를 입출력하는 것이 빠름
  • BufferedInputStream
    • read() 호출 시 입력소스로부터 버퍼 크기만큼 데이터를 읽어다 자신 내부 버퍼에 저장. 외부 입력소스로부터 읽는 것보다 내부 버퍼로 부터 읽는 것이 훨씬 빠름
  • BufferedOutputStream
    • write() 호출시 내부 버퍼에 출력할 내용을 저장. 버퍼가 가득 차거나 flush()를 호출했을 때 버퍼 내용을 출력소스에 출력하고 버퍼를 비움
    • close() 호출시 내부적으로 flush() 호출
public class BufferedOutputStreamEx1 {
    public static void main(String[] args) {
        try {
            FileOutputStream fos = new FileOutputStream("123.txt");
            BufferedOutputStream bos = new BufferedOutputStream(fos, 5);
            for (int i = '1'; i < '9'; i++) {
                bos.write(i);
            }
            bos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

NIO

  • Channel : Stream과 달리 양방향성. 즉, InputChannel , OutputChannel을 따로 만들 필요 없음.
  • Buffer 를 통해서만 읽고 쓰기 가능
  • 논블로킹, 블로킹 모두 가능
    • blocking : 기존 Stream 방식의 IO. Thread가 read(), write() 호출시 해당 Thread는 available한 data가 있거나 data가 완전히 써질 때까지 blocked 됨
    • non-blocking : data가 read되거나 write 될 때까지 기다리지 않음.
스크린샷 2022-03-05 오후 6 16 03

Java NIO(New I/O) Vs. IO. The sections Iam going to explain are | by Nilasini Thirunavukkarasu | Medium

  • Selector
    • 하나의 Thread가 여러 개의 channel을 모니터링.
    • input이 available한 channel이 있거나 writing할 준비가 된 channel이 있으면 알려줌.

InputStream과 OutputStream

  • 스트림은 바이트 단위로 데이터를 전송
  • 입출력 대상에 따라 입출력 스트림 종류가 결정됨
    • FileInputStream / FileOutputStream: 파일 전송
    • ByteArrayInputStream / ByteArrayOutputStream : 메모리 전송
    • PipedInputStream / PipedOutputStream: 프로세스 간 통신
    • AudioInputStream / AudioOutputStream : 오디오
    • 위 클래스들은 모두 InputStream / OutputStream을 상속
public class IOEx1 {

    public static void main(String[] args) {
        byte[] inSrc = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
        byte[] outSrc = null;

        ByteArrayInputStream input = null;
        ByteArrayOutputStream output = null;

        input = new ByteArrayInputStream(inSrc);
        output = new ByteArrayOutputStream();

        int data = 0;

        while ((data = input.read()) != -1) {
            output.write(data);
        }

        outSrc = output.toByteArray();

        System.out.println("inSrc = " + Arrays.toString(inSrc));
        System.out.println("outSrc = " + Arrays.toString(outSrc));

    }
}
public class IOEx2 {

    public static void main(String[] args) {
        byte[] inSrc = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
        byte[] outSrc = null;
        byte[] temp = new byte[10];

        ByteArrayInputStream input = null;
        ByteArrayOutputStream output = null;

        input = new ByteArrayInputStream(inSrc);
        output = new ByteArrayOutputStream();

        input.read(temp, 0, temp.length);    //읽어 온 데이터를 temp에 담는다.
        output.write(temp, 5, 5);    //temp[5]부터 5개의 데이터를 write

        outSrc = output.toByteArray();

        System.out.println("inSrc = " + Arrays.toString(inSrc));
        System.out.println("temp = " + Arrays.toString(temp));
        System.out.println("outSrc = " + Arrays.toString(outSrc));

    }

}
inSrc = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
temp = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
outSrc = [5, 6, 7, 8, 9]

InputStream

  • abstract int read()
    • 입력스트림으로부터 1 byte를 읽어서 반환. 읽을 수 없으면 -1을 반환
  • int read(byte[] b)
    • 입력스트림으로부터 b의 크기만큼 데이터를 읽어서 배열 b에 저장
  • int read(byte[] b, int off, int len)
    • 입력스트림으로부터 len개의 byte를 읽어서 byte배열 b의 off위치부터 저장
  • int available()
    • 스트림으로부터 읽어 올 수 있는 데이터의 크기를 반환
  • void close()
    • 스트림을 닫고 사용하고 있던 자원을 반환
  • void mark(int readlimit)
    • 현재 위치를 표시. 나중에 reset() 에 의해 표시해 놓은 위치로 돌아갈 수 있음. readlimit은 되돌아갈 수 있는 byte의 수를 의미.
  • boolean markSupported()
    • mark() , reset() 의 지원 여부
  • void reset()
    • 스트림에서의 위치를 마지막으로 mark()가 호출된 위치로 되돌림
  • long skip(long n)
    • 스트림에서 주어진 길이(n) 만큼을 건너뜀

OutputStream

  • abstract void write(int b)
    • 주어진 값을 출력소스에 씀 (b는 무슨 역할?)
  • void write(byte[] b)
    • 주어진 배열 b 에 저장된 모든 내용을 출력소스에 씀
  • void write(byte[] b, int off, int len)
    • 주어진 배열 b에 저장된 내용 중에서 off번째부터 len개 만큼만을 읽어서 출력소스에 씀
  • void close()
    • 입력소스를 닫음으로써 사용하고 있던 자원을 반환
  • void flush()
    • 스트림의 버퍼에 있던 모든 내용을 출력소스에 씀
    • 버퍼가 있는 출력스트림에서만 의미가 있음. OutputStream에서는 아무런 기능X

Byte와 Character 스트림

Character 스트림

  • Java 에서 char은 2 byte
    • 바이트기반 stream 으로 문자를 처리하는 것은 어려움이 있음
  • FileReader / FileWriter
  • CharArrayReader / CharArrayWriter
  • PipedReader / PipedWriter
  • StringReader / StringWriter

Reader

  • int read()
    • 입력소스로부터 하나의 문자를 읽어 옴. char의 범위인 0~65536 범위의 정수를 반환. 입력스트림의 마지막 데이터에 도달 시 -1 반환
  • int read(char[] c)
    • 입력소스로부터 매개변수로 주어진 배열 c의 크기만큼 읽어서 배열 c에 저장. 읽어 온 데이터의 개수 혹은 -1을 반환
  • abstract int read(char[] c, int off, int len)
    • 입력소스로부터 최대 len개의 문자를 읽어서 배열 c의 off 번째 위치에 읽은 만큼 저장한다. 읽어온 데이터의 개수 혹은 -1 반환

Writer

  • void write(int c)
    • 주어진 값을 출력소스에 쓴다
  • void write(char[] c)
    • 배열 c에 저장된 모든 내용을 출력소스에 쓴다
  • abstract void write(char[] c, int off, int len)
    • 배열 c에 저장된 내용 중에서 off번째부터 len길이만큼만 출력소스에 쓴다
  • void write(String str)
    • 주어진 문자열을 출력소스에 쓴다
  • void write(String str, int off, int len)
    • 주어진 문자열의 일부를 출력소스에 쓴다. (off번째 문자부터 len개 만큼의 문자열)
public class BufferedReaderEx1 {
    //';' 을 포함한 line을 출력하는 예제
    public static void main(String[] args) {
        try {

            FileReader fr = new FileReader("BufferedReaderEx1.java");
            BufferedReader br = new BufferedReader(fr);

            String line = "";
            for (int i = 1; (line = br.readLine()) != null ; i++) {
                if (line.indexOf(";") != -1) {
                    System.out.println(i + ":" + line);
                }
            }

            br.close();
        } catch (IOException e) {

        }
    }
}

표준 입출력 (System.in, System.out, System.err)

  • 콘솔을 통한 데이터 입력과 데이터 출력을 의미
  • 자바에서는 표준 입출력을 위해 3가지 입출력을 제공.
    • 자바 어플리케이션 실행과 동시에 자동적으로 생성되기 때문에 개발자가 별도로 스트림을 생성하는 코드를 작성할 필요 없음
  • 내부적으로 BufferedInputStream, BufferedOutputStream의 인스턴스를 사용
  • System.in
    • 콘솔로부터 데이터를 입력받는데 사용
  • System.out
    • 콘솔로 데이터를 출력하는데 사용
  • System.err
    • 콘솔로 데이터를 출력하는데 사용
public class StandardIOEx1 {

    public static void main(String[] args) throws IOException {
        int input = 0;
        while ((input = System.in.read()) != -1) {
            System.out.println("input : " + input + ", (char)input : " + (char)input);
        }
    }
}
hello
input : 104, (char)input : h
input : 101, (char)input : e
input : 108, (char)input : l
input : 108, (char)input : l
input : 111, (char)input : o
input : 10, (char)input : 

파일 읽고 쓰기

File 클래스

  • 파일과 디렉토리를 다룸
  • File(String fileName)
    • 주어진 문자열을 이름으로 갖는 파일(혹은 디렉토리)을 위한 File 인스턴스 생성
    • fileName은 주로 경로를 포함해서 지정. 파일이름만 명시하면 프로그램이 실행되는 위치가 경로로 간주됨
public class FileEx2 {

    public static void main(String[] args) {
        if (args.length != 1) {
            System.out.println("USAGE : java FileEx2 DIRECTORY");
            System.exit(0);
        }

        File f = new File(args[0]);

        if (!f.exists() || !f.isDirectory()) {
            System.out.println("Invalid Directory!");
            System.exit(0);
        }

        File[] files = f.listFiles();

        for (int i = 0; i < files.length; i++) {
            String fileName = files[i].getName();
            System.out.println(files[i].isDirectory() ? "[" + fileName + "]" : fileName);
        }
    }

}

기선님 리뷰 영상

  • read() 의 반환값은 int (4 bytes) 지만 InputStream / OutputStream 은 그 중 일부인 1 byte만 읽어 옴. char 기반의 Stream의 경우는 그 중 2 bytes만 읽어 옴.
  • BufferedStream을 쓰면 속도가 빨라지는 이유
    • OS 레벨에서 system call 횟수를 줄이기 때문에 (물 뜨러 주방에 가는 것에 비유)
  • (업무 팁) blocked 되는 순간 질문하는 것이 좋다.
  • direct buffer / non-direct buffer 학습 후 creating a Java off-heap in memory database 아티클 읽어보기

References

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함