Java의 입출력 스트림 정리

31160 단어 Javatech
지금까지 분위기 속에서 자바를 사용하고 있는 입출력 흐름 주변 학급에서 무엇이 어떤 역할을 했는지 혼란스러워 정리한 기사이기 때문이다.

전제 조건


이번에 기재된 Java API는 Java11을 기반으로 합니다.

이번에 정리하고 싶은 주변 반.


이번에 정리된 대상은 이하 학급이다.
  • InputStream
  • OutputStream
  • FileInputStream
  • FileOutputStream
  • BufferedInputStream
  • BufferedOutputStream
  • Reader
  • Writer
  • InputStreamReader
  • OutputStreamWriter
  • FileReader
  • FileWriter
  • BufferedReader
  • BufferedWriter
  • 원래 소위 흐르는


    흐름(영:stream)은 연속된 데이터를'흐르는 데이터'로 포착하고 이 데이터의 입력과 출력, 수발, 그리고 이 조작에 사용되는 추상적인 데이터 유형을 처리하는 것을 말한다[1].출력 흐름(output stream)을 이용하여 데이터를 쓰고 입력 흐름(input stream)을 이용하여 데이터를 읽습니다.파일의 입력과 출력을 처리하는 것, 메모리 버퍼의 입력과 출력을 처리하는 것, 네트워크 통신을 처리하는 것 등 각양각색의 것이 있다.
    흐름(프로그래밍) - 위키백과
    흐름이란 파일, 네트워크, 메모리의 데이터를 읽기, 쓰기, 또는 조작할 수 있는 인터페이스를 말한다.

    먼저 InputStream과 Reader/Writer를 분리합니다.


    상기 학급에서 인풋스트림, 아웃풋스트림, 리더, Writer는 추상류이다.이 때문에 모든 학과의 학급은 기본적으로 이 네 가지 유형 중 하나를 계승했다.
    InputStream
     |-- FileInputStream
     |-- ByteArrayInputStream
     |-- FilterInputStream
          |-- BufferedInputStream
    
    OutputStream
     |-- FileOutputStream
     |-- ByteArrayOutputStream
     |-- FilterOutputStream
          |-- BufferedOutputStream
    
    Reader
     |-- InputStreamReader
          |-- FileReader
     |-- BufferedReader
     |-- StringReader
    
    Writer
     |-- OutputStreamWriter
          |-- FileWriter
     |-- BufferedWriter
     |-- StringWriter
     |-- PrintWriter
    
    InputStream/OutputStream은 바이트 단위로 데이터를 처리합니다.InputStream은 읽기, OutputStream은 쓰기입니다.
    Reader/Writer는 데이터를 문자 단위로 처리합니다.Reader는 읽기, Writer는 쓰기입니다.
    이후 각 부분을 꼼꼼히 살펴본다.

    InputStream/OutputStream


    데이터의 흐름을 바이트 단위로 처리하다.바이트 흐름이라고도 부른다.
    InputStream/OutputStream은 바이트 단위로 데이터를 처리하기 때문에 바이너리 데이터의 읽기와 쓰기에 자주 사용된다고 생각합니다.
    텍스트 데이터의 읽기와 쓰기도 사용할 수 있지만, 후술한 Reader/Writer를 사용하면 좋은 점이 있기 때문에 Reader/Writer를 사용하십시오. 텍스트 데이터를 사용하십시오.
    InputStream/OutputStream에는 다음과 같은 설치 클래스가 있습니다.
  • FileInputStream/FileOutputStream
  • ByteArrayInputStream/ByteArrayOutputStream
  • ZipInputStream/ZipOutputStream
  • BufferedInputStream/BufferedOutputStream
  • 참고 자료
  • InputStream (Java SE 11 & JDK 11 )
  • OutputStream (Java SE 11 & JDK 11 )
  • Reader/Writer


    데이터 흐름을 문자 단위로 처리합니다.문자류라고도 부른다.
    InputStream/OutputStream은 일본어와 같은 멀티바이트 문자를 읽을 때 지정된 바이트 수에 따라 문자의 중간에만 읽지 않고 표시할 때 혼란이 발생하는 등 바이트 단위로 데이터를 처리한다.
    일본어와 같은 멀티바이트 문자를 읽으려면 Reader/Writer를 사용하는 것이 좋습니다.또는 싱글바이트 문자도 특별히 InputStream/OutputStream이 사용하는 장점을 선택하지 않았기 때문에 기본적으로 문자를 사용할 때 Reader/Writer를 사용하면 된다.
    Reader/Writer에는 다음과 같은 인스턴스 클래스가 있습니다.
  • InputStreamReader/OutputStreamWriter
  • FileReader/FileWriter
  • BufferedReader/BufferedWriter
  • StringReader/StringWriter
  • PrintWriter
  • 참고 자료
  • Reader (Java SE 11 & JDK 11 )
  • Writer (Java SE 11 & JDK 11 )
  • 지금까지 InputStream/OutputStream, Reader/Writer가 정리되어 있으며 앞으로 각 설치 클래스에 대해 몇 가지 선택을 진행할 것입니다.

    InputStream/OutputStream의 설치 클래스


    FileInputStream/FileOutputStream


    파일에서 데이터를 읽거나 파일에 쓰기 위한 바이트 흐름입니다.
    파일에 바이너리 데이터를 읽거나 쓰려면 이것을 사용하십시오.
    설치 예
    byte[] readData = new byte[1024];
    try (
            FileInputStream fis = new FileInputStream("./src/read.png");
            FileOutputStream fos = new FileOutputStream("./out/written.png")) {
        int readBytes;
        // readData配列の長さ(バイト数)分読み込んで書き込む
        // これをファイルの最後を読み込むまで繰り返す
        while ((readBytes = fis.read(readData)) != -1) {
            fos.write(readData, 0, readBytes);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    
  • FileInputStream (Java SE 11 & JDK 11 )
  • FileOutputStream (Java SE 11 & JDK 11 )
  • BufferedInputStream/BufferedOutputStream


    버퍼링 데이터를 읽으면서 쓰는 바이트 흐름입니다.
    위의 FileInputStream/FileOutputStream은read/write를 실행하는 횟수에만 파일에 대한 입출력이 발생하기 때문에 효율이 떨어집니다.
    BufferedInputStream/BufferedOutputStream은 데이터를 일정량의 버퍼 메모리(버퍼 후)에 저장하여 처리하기 때문에 입출력 횟수를 줄이고 읽기와 쓰기를 효과적으로 할 수 있다.
    사용 시 InputStream/OutputStream을 지정하여 각각의 구조기에 흐름을 생성합니다.
    설치 예
    byte[] readData = new byte[1024];
    try (
            // コンストラクタ引数にFileInputStreamを指定
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream("./src/read.png"));
    	// コンストラクタ引数にFileOutputStreamを指定
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("./out/written.png"))) {
        int readBytes;
        while ((readBytes = bis.read(readData)) != -1) {
            bos.write(readData, 0, readBytes);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    
  • BufferedInputStream (Java SE 11 & JDK 11 )
  • BufferedOutputStream (Java SE 11 & JDK 11 )
  • Reader/Writer 설치 클래스


    InputStreamReader/OutputStreamWriter


    바이트 흐름과 문자 흐름의 흐름을 변환합니다.
    즉, InputStream, OutputStream을 각각 Reader, Writer로 포장함으로써 사용자는 바이트 흐름이 아닌 문자 흐름을 처리할 수 있다.
    InputStream Reader는 바이트 스트림을 문자 스트림으로 변환합니다.
    흐름에서 바이트를 읽은 후 지정한charset으로 문자를 디코딩합니다.따라서 사용자는 이를 문자 흐름으로 읽을 수 있다.
    OutputStream Writer는 문자 스트림을 바이트 스트림으로 변환합니다.
    문자를 흐름에 쓴 후 지정한charset을 사용하여 바이트로 인코딩합니다.따라서 사용자는 그것을 문자 흐름으로 쓸 수 있다.
    사용 시 InputStream/OutputStream을 지정하여 각각의 구조기에 흐름을 생성합니다.
    설치 예제(InputStream Reader)
    char[] readData = new char[32];
    try (
        // コンストラクタ引数にFileInputStreamを指定
        InputStreamReader isr = new InputStreamReader(new FileInputStream("./src/japanese.txt"))) { // 「私は日本人です」と書かれたファイルを読み込む
        int readChars = isr.read(readData, 0, 5); // 0文字目から5文字分読み込む
        System.out.println(readChars); // 5
        System.out.println(new String(readData)); // 私は日本人
    } catch (IOException e) {
        e.printStackTrace();
    }
    
  • InputStreamReader (Java SE 11 & JDK 11 )
  • OutputStreamWriter (Java SE 11 & JDK 11 )
  • FileReader/FileWriter


    파일에 읽을 텍스트 흐름입니다.
    내부 설치는 신선도 유지new InputStreamReader(new FileInputStream("..."))new OutputStreamWriter(new FileOutputStream("..."))에 불과하다.
    이걸 조금 간단하게 쓸 수 있는 리더/Writer가 되겠습니다.
    설치 예제(FileReader)
    하는 일은 위의 실시례와 같다.
    char[] readData = new char[32];
    try (FileReader reader = new FileReader("./src/japanese.txt")) { // 「私は日本人です」と書かれたファイルを読み込む
        int readChars = reader.read(readData, 0, 5); // オフセット0文字目から5文字分読み込む
        System.out.println(readChars); // 5
        System.out.println(new String(readData)); // 私は日本人
    } catch (IOException e) {
        e.printStackTrace();
    }
    

    BufferedReader/BufferedWriter


    데이터를 버퍼링하면서 읽는 텍스트 흐름입니다.
    상기 InputStream Reader/OutputStream Writer와 FileReader/FileWriter는read/write를 실행하는 횟수만 파일에 입출력을 하기 때문에 효율이 떨어진다.
    BufferedReader/BufferedWriter는 데이터를 일정량의 버퍼 메모리 (버퍼 후) 에 저장하여 처리하기 때문에 입출력 횟수를 줄이고 읽기와 쓰기를 효과적으로 할 수 있습니다.
    BufferedInputStream, BufferedOutpuStream과 구조가 같습니다.
    사용 시 Reader/Writer를 개별 구조기로 지정하여 흐름을 생성합니다.
    설치 예제(BufferedReader)
    char[] readData = new char[32];
    try (BufferedReader br = new BufferedReader(new FileReader("./src/japanese.txt"))) { // 「私は日本人です」と書かれたファイルを読み込む
        // まずバッファに、ある十分な量(デフォルトは8192文字分)読み込まれる
        // その後バッファから指定文字数(今回は5文字分)読み込む
        int readChars = br.read(readData, 0, 5); // オフセット0文字目から5文字分読み込む
        System.out.println(readChars); // 5
        System.out.println(new String(readData)); // 私は日本人
        
        // 以降はバッファから読み込む
        // バッファに格納されたデータより先のデータを読み込むときに再度十分な量をバッファに読み込んでからバッファから読み込む
        readChars = br.read(readData, readChars, 2);
        System.out.println(readChars); // 2
        System.out.println(new String(readData)); // です
    } catch (IOException e) {
        e.printStackTrace();
    }
    

    주제 밖의 말1: 버퍼링 리더는 어느 시간에 버퍼링을 합니까


    OpenJDK 11.0.7로 인코딩을 추적해 보았고read의 정시에 진행되었다.
    다음fill 방법은 버퍼 처리에 적용되지만 버퍼 메모리에 저장된 데이터를 읽기 전의 데이터를 읽을 때 호출됩니다.
    private int read1(char[] cbuf, int off, int len) throws IOException {
        if (nextChar >= nChars) {
        /* If the requested length is at least as large as the buffer, and
           if there is no mark/reset activity, and if line feeds are not
           being skipped, do not bother to copy the characters into the
           local buffer.  In this way buffered streams will cascade
           harmlessly. */
            if (len >= cb.length && markedChar <= UNMARKED && !skipLF) {
                return in.read(cbuf, off, len);
            }
            fill();
        }
        if (nextChar >= nChars) return -1;
        if (skipLF) {
            skipLF = false;
            if (cb[nextChar] == '\n') {
                nextChar++;
                if (nextChar >= nChars)
                    fill();
                if (nextChar >= nChars)
                    return -1;
            }
        }
        int n = Math.min(len, nChars - nextChar);
        System.arraycopy(cb, nextChar, cbuf, off, n);
        nextChar += n;
        return n;
    }
    
    https://github.com/openjdk/jdk11u/blob/jdk-11.0.7-ga/src/java.base/share/classes/java/io/BufferedReader.java#L202-L229
    버퍼 처리
    https://github.com/openjdk/jdk11u/blob/jdk-11.0.7-ga/src/java.base/share/classes/java/io/BufferedReader.java#L128-L167
    반면 버퍼에 데이터가 있으면 버퍼에서만 읽을 수 있어 입출력 횟수를 줄일 수 있다.

    주제 외 2: BufferedReader 한 번 버퍼링 수량


    OpenJDK 11.0.7이 확인되면 기본값은 8192자입니다.
    https://github.com/openjdk/jdk11u/blob/jdk-11.0.7-ga/src/java.base/share/classes/java/io/BufferedReader.java#L88
    그러나 이 값은 BufferedReader의 구조기의 두 번째 매개 변수로 변경할 수 있습니다.따라서 8192자 이상을 한 번에 읽으려면 이 값을 변경할 수 있다.
    https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/io/BufferedReader.html#<init>(java.io.Reader,int)

    총결산


    InputStream/OutputStream
    Reader/Writer
    과업
    바이트 스트림 처리
    스트림 작업
    실현 클래스
    FileInputStream/FileOutputStream BufferedInputStream/BufferedOutputStream
    InputStreamReader/OutputStreamWriter FileReader/FileWriter BufferedReader/BufferedWriter

    좋은 웹페이지 즐겨찾기