개발공부/JAVA

[JAVA] I/O Stream

jnnjnn 2024. 3. 27. 19:48

입출력 스트림

I/O Stream은 데이터 Input과 output을 실행할 수 있는 입출력 스트림으로 java.io 패키지에서 제공된다.

다음 두 종류로 구분할 수 있다

  • 바이트 스트림 : 그림, 멀티미디어, 문자 등 모든 종류의 데이터 입출력할 때 사용 InputStream, OutputStream
  • 문자 스트림 : 문자만 입출력할 때 사용 Reader, Writer

바이트 출력 스트림

  • OutputStream은 바이트 출력 스트림의 최상위 클래스로 추상 클래스이다.
  • 모든 바이트 출력 스트림 클래스는 OutputStream 클래스를 상속받아 만들어진다
  • OutputStream이 추상 클래스이므로 객체는 OutputStream의 자식 객체를 사용하여 생성한다
  • IOException을 발생시키므로 예외 처리가 필요하다
  • 사용 후 close() 메소드를 호출해서 출력 스트림이 사용했던 메모리를 해제해야 한다

OutputStream 클래스의 주요 메소드

리턴 타입 메소드 설명
void write(int b) 1byte를 출력
void write(byte[] b) 매개값으로 주어진 배열 b의 모든 바이트를 출력
void write(byte[] b, int off, int len) 매개값으로 주어진 배열 b[off]부터 len개의 바이트를 출력
void flush() 출력 버퍼에 잔류하는 모든 바이트를 출력
void close() 출력 스트림을 닫고 사용 메모리 해제
public class C02OutputStream {
    public static void main(String[] args) throws IOException {
        // temp/output2.data로 출력 데이터 내보냄
        OutputStream os = new FileOutputStream("temp/output2.data");

        os.write(29374); // 1 byte 출력
        byte[] array = {10, 20, 30, 40, 50};
        os.write(array); // 배열 array 출력
        os.write(array, 1, 3); // 1번 인덱스부터 3개까지 출력

        os.flush(); // 아직 스트림에 출력되지 않은 데이터들을 강제로 출력
        os.close(); // 꼭 작성해야함
    }
}

바이트 입력 스트림

  • InputStream은 바이트 입력 스트림의 최상위 클래스로, 추상 클래스이다
  • 모든 바이트 입력 스트림은 InputStream 클래스를 상속받아 만들어진다

InputStream의 주요 메소드

리턴 타입 메소드 설명
int read() 1byte를 읽은 후 읽은 바이트값을 int로 리턴
int read(byte[] b) 읽은 바이트를 매개값으로 주어진 배열에 저장 후 읽은 바이트 수를 리턴
void close() 입력 스트림을 닫고 사용 메모리 해제

더 이상 입력 스트림으로부터 바이트를 읽을 수 없다면 read() 메소드는 -1을 리턴한다

public class C02InputStream {
    public static void main(String[] args) {
        String fileName = "temp/test3.db";

        try (InputStream is = new FileInputStream(fileName)) {
            int data = 0;

            while ((data = is.read()) != -1) { // 더 이상 읽을 바이트가 없을 때까지 반복
                System.out.println("data = " + data); // 읽은 바이트 출력
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

try-with-resources

  • try블록에 close()를 사용할 수 없음
  • close()를 사용하기 위해 코드가 복잡해짐
  • 자바 7부터는 try-with-resources를 사용하여 간편하게 리소스를 해제할 수 있다
public class C04TryWithResources {
    public static void main(String[] args) {
        // close 메소드 호출을 위한 코드를 줄이기 위한 문법

        String fileName = "temp/output4.data";

        // try-with-resources
        // try () 괄호 안에서 선언된 객체는 자동으로 close 메소드를 호출해줌
        try (OutputStream os = new FileOutputStream(fileName)) {
            os.write(293748);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

문자 입출력 스트림

문자 입출력 스트림으로는 ReaderWriter가 있다. 입출력되는 단위가 문자인 것을 제외하고는 바이트 입출력 스트림과 사용 방법은 동일하다.

문자 출력

  • Writer는 문자 출력 스트림의 최상위 클래스로, 추상 클래스이다.
  • 모든 문자 출력 스트림 클래스는 Writer 클래스를 상속받아서 만들어진다

Writer의 주요 메소드

리턴 타입 메소드 설명
void write(int c) 매개값으로 주어진 한 문자를 출력
void write(char[] cbuf) 매개값으로 주어진 배열의 모든 문자를 출력
void write(char[] cbuf, int off, int len) 매개값으로 주어진 배열에서 cbuf[off]부터 len개까지의 문자를 출력
void write(String str) 매개값으로 주어진 문자열을 출력
void write(String str, int off, int len) 매개값으로 주어진 문자열에서 off 순번부터 len개까지의 문자를 출력
void flush() 버퍼에 잔류하는 모든 문자를 출력
void close() 출력 스트림을 닫고 사용 메모리를 해제
public class WriteExample {
    public static void main(String[] args) {
        try {
            Writer writer = new FileWriter("temp/test.txt");

            char a = 'A';
            writer.write(a);
            char b = 'B';
            writer.write(b);

            char[] arr = {'C', 'B', 'E'};
            writer.write(arr);

            writer.write("FGH");

            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

문자 읽기

  • Reader는 문자 입력 스트림의 최상위 클래스로, 추상 클래스이다
  • 모든 문자 입력 스트림 클래스는 Reader 클래스를 상속받아서 만들어진다

Reader의 주요 메소드

리턴 타입 메소드 설명
int read() 1개의 문자를 읽고 리턴
int read(char[] cbuf) 읽은 문자들을 매개값으로 주어진 무자 배열에 저장하고 읽은 문자 수를 리턴
void close() 입력 스트림을 닫고, 사용 메모리 해제
public class ReadExample {
    public static void main(String[] args) {
        try (Reader reader =new FileReader("temp/output2.txt")){
            while (true) {
                int data = reader.read();
                if (data == -1) break;
                System.out.println((char) data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

보조 스트림

  • 보조 스트림이란 다른 스트림과 연결되어 여러 가지 편리한 기능을 제공해주는 스트림을 말한다
  • 자체적으로 입출력을 수행할 수 없기 때문에 입출력 소스로부터 직접 생성된 입출력 스트림에 연결해서 사용해야 한다
  • 보조 스트림은 또 다른 보조 스트림과 연결되어 스트림 체인으로 구성할 수 있다

문자 변환 스트림

InputStreamReader로, OutputStreamWriter로 변환할 수 있다

public class C02OutputStreamWriter {
    public static void main(String[] args) throws Exception {
        String file = "temp/output2.txt";
        OutputStream os = new FileOutputStream(file);

        // OutputStreamWriter :
        // 문자 단위 출력 스트림을 바이트 단위 출력 스트림 연결

        OutputStreamWriter osw = new OutputStreamWriter(os);
        char c1 = 'A';
        osw.write(c1);

        char c2 = '가';
        osw.write(c2);

        osw.close();
    }
}

성능 향상 스트림

  • 입출력 버퍼 스트림을 사용하면 버퍼에 데이터가 쌓이기를 기다렸다가 꽉 차게 되면 데이터를 한꺼번에 하드 디스크로 보낸다
  • 결과적으로 출력 횟수를 줄여주어 성능이 향상된다
public class C04BufferedOutputStream {
    public static void main(String[] args) throws IOException {
        String fileName = "temp/bigfile/output2.data";
        OutputStream os = new FileOutputStream(fileName);
        BufferedOutputStream bos = new BufferedOutputStream(os);

        byte[] data = new byte[1024];

        long start = System.nanoTime();
        for (int i = 0; i < (1024 * 1024); i++) {
            bos.write(data);
        }
        bos.flush();
        bos.close();

        long end = System.nanoTime();
        long time = end - start;
        System.out.println("time = " + time);
    }
}

프린트 스트림

  • 바이트 출력 스트림인 PrintStream과 문자 출력 스트림인 PrintWriter가 있다.
  • System.out.println()outPrintStream 타입이다
  • 프린트 스트림의 메소드로는 print(), println(), printf() 가 있다
public class PrintStreamExample {
    public static void main(String[] args) throws FileNotFoundException {
        FileOutputStream fos = new FileOutputStream("temp/printstream.txt");
        PrintStream ps = new PrintStream(fos);

        ps.print("마치");
        ps.println("프린트가 출력하는 것처럼");
        ps.println("출력합니다");
        ps.printf("|%6|%-10s|%10s|\n", 1, "홍길동,도적");
        ps.printf("|%6|%-10s|%10s|\n", 1, "김자바,학생");

        ps.flush();
        ps.close();
    }
}

File과 Files 클래스

  • Filejava.io패키지에, Filesjava.nio.file패키지에 속한다
  • FilesFile을 개선한 클래스로, 좀 더 많은 기능을 가지고 있다
  • path에 파일이나 디렉토리가 없더라도 File 객체를 생성할 수 있다
  • 생성한 File 객체는 exists() 메소드로 실제 파일이나 디렉토리의 존재 유무를 확인할 수 있다
  • Files클래스는 정적 메소드로 구성되어 있어 객체를 생성할 필요가 없다
exists() 메소드의 리턴값이 false인 경우
리턴 타입 메소드 설명
boolean createNewFile() 새로운 파일을 생성
boolean mkdir() 새로운 디렉토리를 생성
boolean mkdirs() 경로상에 없는 모든 디렉토리를 생성

 

exists() 메소드의 리턴값이 true인 경우

 

리턴 타입 메소드 설명
boolean delete() 파일 또는 디렉토리 삭제
String getNaem() 파일의 이름을 리턴
String getParent() 부모 디렉토리를 리턴
File getParentFile() 부모 디렉토리를 파일 객체로 생성 후 리턴
String getPath() 전체 경로를 리턴
boolean isDirectory() 디렉토리인지 여부
boolean isFile() 파일인지 여부
long length() 파일의 크기 리턴
String[] list() 디렉토리에 포함된 파일 및 서브 디렉토리 목록 전부를 String 배열로 리턴
public class FileExample {
    public static void main(String[] args) throws IOException {
        File dir = new File("temp/images");
        File file1 = new File("temp/file1.txt");
        File file2 = new File("temp/file2.txt");
        File file3 = new File("temp/file3.txt");

        if (dir.exists() == false) {
            dir.mkdirs();
        }
        if (file1.exists() == false) {
            file1.createNewFile();
        }
        if (file2.exists() == false) {
            file2.createNewFile();
        }
        if (file3.exists() == false) {
            file3.createNewFile();
        }

        File temp = new File("temp");
        File[] contents = temp.listFiles();

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd a HH:mm");
        for (File file : contents) {
            System.out.printf("%-25s", sdf.format(new Date(file.lastModified())));
            if (file.isDirectory()) {
                System.out.printf("%-10s%-20s", "<DIR>", file.getName());
            } else {
                System.out.printf("%-10s%-20s", file.length(), file.getName());
            }
            System.out.println();
        }
    }
}