Java - I/O (스트림, 파일 입출력 )

INHEES·2025년 2월 10일

금일은 Java 의 I/O 에 대해서 알아보는 시간입니다.

목차

  • 스트림
  • InputStream, OutputStream

스트림

자바 프로세스가 가지고 있는 데이터를 밖으로 보내려면 출력 스트림을 사용하면 되고,
반대로 외부 데이터를 자바 프로세스 안으로 가져오려면 입력 스트림을 사용하면 된다.

아래의 코드를 실행한뒤 hello.dat 파일을 열기 위해서는 속성에서 Text 로 선택하면 됩니다.

해당 65 숫자는 아스키 코드 값으로 인식하여 A 값으로 디코딩되어서 출력되게 됩니다.

외부의 자원을 가져다 쓰는 것이기 때문에 close() 함수를 써주어야 합니다.

    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("temp/hello.dat");
        fos.write(65);
        fos.close();

        FileInputStream fis = new FileInputStream("temp/hello.dat");
        System.out.println(fis.read());
        System.out.println(fis.read());
        fis.close();
    }

new FileOutputStream("temp/hello.dat")

  • 파일에 데이터를 출력하는 스트림이다.
  • 파일이 없으면 파일을 자동으로 만들고, 데이터를 해당 파일에 저장한다.
  • 폴더를 만들지는 않기 때문에 폴더는 미리 만들어두어야 한다.

read()

  • 파일에서 데이터를 byte 단위로 하나씩 읽어온다.

  • 순서대로 65, 66, 67을 읽어온다.

  • 파일의 끝에 도달해서 더는 읽을 내용이 없다면 1을 반환한다.(EOF, End of File)

참고 : 파일 append 옵션

  • true : 기존 파일의 끝에 이어서 쓴다.
  • false: default 값으로 기존 파일의 데이터를 지우고 처음부터 다시 쓴다.
  new FileOutputStream("temp/hello.dat", true);

다음 예제는 파일의 데이터를 읽기 위한 예제 입니다.

    FileInputStream fis = new FileInputStream("temp/hello.dat");

    int data;
    while ((data = fis.read()) != -1) {
        System.out.println(data);
    }

다음 예제는 byte[] 를 사용하여 원하는 크기 많큼 가져오는 예제입니다.


    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("temp/hello.dat");
        byte[] input = {65, 66, 67};
        fos.write(input);
        fos.close();

        FileInputStream fis = new FileInputStream("temp/hello.dat");
        byte[] buffer = new byte[10];
        int readCount = fis.read(buffer, 0, 10);
        // byte[] readBytes = fis.readAllBytes();
        System.out.println("readCount = " + readCount);
        System.out.println(Arrays.toString(buffer));
        fis.close();
    }

출력 스트림

  • write(byte[]) : byte[] 에 원하는 데이터를 담고 write() 에 전달하면 해당 데이터를 한 번에 출력할 수
    있다

입력 스트림

  • read(byte[], offset, lentgh) : byte[] 을 미리 만들어두고, 만들어둔 byte[] 에 한 번에 데이터를
    읽어올 수 있다.

  • byte[] : 데이터가 읽혀지는 버퍼

  • offset : 데이터 기록되는 byte[] 의 인덱스 시작 위치

  • length : 읽어올 byte의 최대 길이

  • 반환 값: 버퍼에 읽은 총 바이트 수 여기서는 3byte를 읽었으므로 3이 반환된다. 스트림의 끝에 도달하여 더 이상 데이터가 없는 경우 -1을 반환

부분으로 나누어 읽기

  • read(byte[], offset, length)

    • 메모리 사용량을 제어 가능합니다.
    • 읽은 내용을 처리하면서 스트림을 계속해서 읽어야 할 경우 적합
  • readAllBytes()

    • 큰 파일의 경우 OutOfMemoryError 발생 가능합니다.

InputStream, OutputStream

현대의 컴퓨터는 대부분 byte 단위로 데이터를 주고 받는다. 참고로 bit 단위는 너무 작기 때문에 byte 단위를 기본으로 사용한다.

이렇게 데이터를 주고 받는 것을 Input/Output(I/O)라 한다

또한 파일에 저장하던 내용을 네트워크에 전달하거나 콘솔에 출력하도록 변경할 때 너무 많은 코드를 변경해야 할 수 있다.

이런 문제를 해결하기 위해 자바는 InputStream , OutputStream 이라는 기본 추상 클래스를 제공한다.

메모리 스트림


    public static void main(String[] args) throws IOException {
        byte[] input = {1, 2, 3};

        // 메모리에 쓰기
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        baos.write(input);

        // 메모리에서 읽기
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        byte[] bytes = bais.readAllBytes();
        System.out.println(Arrays.toString(bytes));
    }

참고로 메모리에 어떤 데이터를 저장하고 읽을 때는 컬렉션이나 배열을 사용하면 되기 때문에, 이 기능은 잘 사용하지않는다.

주로 스트림을 간단하게 테스트 하거나 스트림의 데이터를 확인하는 용도로 사용한다.

콘솔 스트림


    public static void main(String[] args) throws IOException {
        PrintStream printStream = System.out;

        byte[] bytes = "Hello!\n".getBytes(UTF_8);
        printStream.write(bytes);
        printStream.println("Print!");
    }

자주 사용하는 System.out 은 PrintStream 입니다. OutputStream 을 상속받으며 자바가 시작될 때 자동으로 만들어지기 때문에 직접 생성하지 않아도 됩니다.

정리

InputStream, OutputStream 의 추상화의 장점은 일관성, 유연성, 확장성, 재사용성의 특징을 가지고 있습니다.

다음시간에는 BufferedInputStream, DataInputStream 을 사용하여 기본 데이터 타입의 최적화 방법에 대해 알아보겠습니다.


참고자료

inflearn

profile
이유를 찾아보자

0개의 댓글