코딩쿠의 코드 연대기

Java) I/O 본문

코딩스터디/JAVA스터디

Java) I/O

코딩쿠 2024. 11. 19. 23:21

학습목표

  • 스트림 (Stream) / 버퍼 (Buffer) / 채널 (Channel) 기반의 I/O
  • InputStream과 OutputStream
  • Byte와 Character 스트림
  • 표준 스트림 (System.in, System.out, System.err)
  • 파일 읽고 쓰기

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

Java에서 데이터를 입출력하는 방법에는 크게 스트림(Stream), 버퍼(Buffer), 채널(Channel) 기반의 I/O가 있습니다.

1. 스트림 (Stream)

  • 정의: 데이터를 순차적으로 단방향으로 전송하는 방식입니다. 마치 물이 흐르는 파이프처럼 데이터가 흐르는 통로라고 생각하면 됩니다. 데이터의 읽기(InputStream) 또는 쓰기(OutputStream) 작업을 처리합니다. 데이터의 읽기(InputStream) 또는 쓰기(OutputStream) 작업을 처리합니다.
  • 특징:
    • 단방향: 입력 스트림(InputStream)은 데이터를 읽기만 하고, 출력 스트림(OutputStream)은 데이터를 쓰기만 합니다.
    • 순차적 접근: 데이터를 처음부터 순서대로 읽거나 써야 합니다. 즉, 특정 위치로 건너뛰거나 이전 위치로 돌아갈 수 없습니다.
    • 블로킹 I/O: 스트림 작업은 기본적으로 작업이 끝날 때까지 현재 스레드를 차단합니다.
    • 다양한 종류 제공: 바이트 기반 스트림(FileInputStream, FileOutputStream)과 문자 기반 스트림(FileReader, FileWriter)이 있습니다.

2. 버퍼 (Buffer)

  • 정의: 데이터 처리 속도를 개선하기 위해 데이터를 임시로 저장하는 임시 메모리 공간입니다.
  • 특징:
    • 데이터 모아서 처리(블록 단위 처리): 버퍼는 데이터를 모아 한 번에 처리함으로써 입출력 성능을 향상시킵니다.
    • 스트림과 조합: BufferedInputStream, BufferedReader, BufferedOutputStream 등 스트림 클래스와 함께 사용됩니다.
    • 효율적인 처리: 디스크 또는 네트워크와 같은 입출력 장치에서 데이터를 읽고 쓰는 작업의 빈도를 줄입니다.

3. 채널 (Channel)

  • 정의: NIO(New Input/Output)에서 도입된 새로운 입출력 방식으로, 데이터를 양방향으로 전송할 수 있는 통로입니다.
  • 특징:
    • 양방향: 하나의 채널을 통해 데이터를 읽고 쓸 수 있습니다.
    • 비블로킹 I/O: 데이터를 읽거나 쓸 때, 작업이 완료될 때까지 기다리지 않고 다른 작업을 수행할 수 있습니다.
    • 버퍼와 함께 사용: 채널은 항상 버퍼와 함께 사용됩니다. 채널을 통해 데이터를 읽거나 쓸 때, 버퍼를 이용하여 데이터를 주고받습니다.(ByteBuffer, CharBuffer 등)
    • 실렉터와 함께 사용: 여러 채널을 효율적으로 관리하기 위해 실렉터(Selector)를 사용할 수 있습니다. Selector를 사용하여 여러 채널을 하나의 스레드로 관리할 수 있어 효율적입니다.

스트림 vs 채널

특징 스트림 채널
데이터 방향 단방향 양방향
블로킹 여부 블로킹 비블로킹
버퍼 사용 여부 선택적 필수
셀렉터 사용 여부 불가능 가능

정리

  • 스트림: 기본적인 입출력 방식으로, 단방향, 블로킹 방식으로 동작합니다.
  • 버퍼: 스트림의 성능을 향상하기 위해 사용되는 임시 저장 공간입니다.
  • 채널: NIO에서 도입된 새로운 입출력 방식으로, 양방향, 비블로킹 방식으로 동작하며, 항상 버퍼와 함께 사용됩니다.

추가 정보:

  • 자바 NIO는 네트워크 프로그래밍과 같이 많은 양의 데이터를 처리해야 하는 경우에 유용합니다.
  • 스트림 기반 I/O: 파일이나 네트워크를 단순하게 읽고 쓸 때 적합합니다.
  • java.io 패키지는 스트림 기반의 I/O 클래스를 제공하고, java.nio 패키지는 채널 기반의 I/O 클래스를 제공합니다.
  • 채널 기반 I/O (NIO): 대규모 데이터 처리, 고성능 네트워크 애플리케이션에 적합합니다.
  • 스트림의 블로킹 I/O
    스트림은 블로킹 방식으로 동작하지만, 자바 8 이후로는 java.util.stream API를 활용해 데이터 처리 방식에 대한 선택권이 있습니다. 하지만 이는 NIO의 비블로킹 I/O와는 별개의 개념입니다.
  • NIO와 버퍼
    버퍼는 NIO에서 핵심적인 역할을 하며, 스트림보다 메모리 사용 및 성능 관리가 더 효율적입니다. 예를 들어, ByteBuffer는 데이터를 읽고 쓸 때 동일한 메모리를 활용합니다.
  • 버퍼의 역할과 제한
    버퍼는 메모리를 활용하므로 크기 제한이 있으며, 크기가 너무 작거나 크면 성능 저하 또는 메모리 낭비가 발생할 수 있습니다.

InputStream과 OutputStream

Java에서 InputStreamOutputStream바이트 기반 스트림을 나타내는 추상 클래스입니다. 이들은 데이터를 바이트 단위로 읽고 쓰는 데 사용됩니다.

1. InputStream

  • 역할: 데이터 소스에서 바이트 단위로 데이터를 읽어오는 역할을 합니다.
  • 주요 메서드:
    • int read(): 스트림에서 한 바이트를 읽어 정수로 반환합니다. 스트림의 끝에 도달하면 -1을 반환합니다.
    • int read(byte [] b): 스트림에서 바이트 배열 b의 크기만큼 바이트를 읽어 배열에 저장하고, 읽은 바이트 수를 반환합니다.
    • int read(byte[] b, int off, int len): 스트림에서 len 바이트만큼 읽어 바이트 배열 boff 위치부터 저장하고, 읽은 바이트 수를 반환합니다.
    • void close(): 스트림을 닫습니다.
  • 구현 클래스: 파일(FileInputStream), 메모리(ByteArrayInputStream), 네트워크 소켓(SocketInputStream) 등 다양한 소스에서 데이터를 읽어오는 클래스들이 InputStream을 상속하여 구현됩니다.

2. OutputStream

  • 역할: 데이터를 바이트 단위로 출력 대상에 쓰는 역할을 합니다.
  • 주요 메서드:
    • void write(int b): 정수 b의 하위 8비트를 바이트로 변환하여 스트림에 씁니다.
    • void write(byte [] b): 바이트 배열 b의 모든 바이트를 스트림에 씁니다.
    • void write(byte[] b, int off, int len): 바이트 배열 boff 위치부터 len 바이트만큼 스트림에 씁니다.
    • void flush(): 스트림의 버퍼에 남아있는 데이터를 모두 출력 대상에 씁니다.
    • void close(): 스트림을 닫습니다.
  • 구현 클래스: 파일(FileOutputStream), 메모리(ByteArrayOutputStream), 네트워크 소켓(SocketOutputStream) 등 다양한 출력 대상에 데이터를 쓰는 클래스들이 OutputStream을 상속하여 구현됩니다.

예제:

Java

import java.io.*;

public class StreamExample {
    public static void main(String[] args) throws IOException {
        // 파일에서 데이터 읽기
        InputStream inputStream = new FileInputStream("input.txt");
        byte[] buffer = new byte[1024];
        int bytesRead;
        while ((bytesRead = inputStream.read(buffe1r)) != -1) {
            // 읽은 데이터 처리
            System.out.println(new String(buffer, 0, bytesRead));
        }
        inputStream.close();

        // 파일에 데이터 쓰기
        OutputStream outputStream = new FileOutputStream("output.txt");
        String data = "Hello, World!";
        outputStream.write(data.getBytes());
        outputStream.close();
    }
}

참고:

  • InputStreamOutputStream은 바이트 기반 스트림이므로, 문자 데이터를 처리하려면 InputStreamReaderOutputStreamWriter를 사용하여 문자 스트림으로 변환해야 합니다.
  • InputStreamOutputStream은 추상 클래스이므로, 직접 객체를 생성할 수 없고, 구현 클래스를 사용해야 합니다.

Byte와 Character 스트림

Java에서 데이터를 입출력하는 스트림은 크게 바이트 스트림문자 스트림으로 나뉩니다. 둘 다 데이터를 순차적으로 읽고 쓰는 기능을 제공하지만, 처리하는 데이터 단위와 용도에 차이가 있습니다.

1. 바이트 스트림 (Byte Stream)

  • 데이터 단위: : 1바이트 (8비트) 단위로 데이터를 처리합니다.
  • 용도: : 이미지, 동영상, 압축 파일 등 이진 데이터를 처리하는 데 적합합니다.
  • 주요 클래스:
    • InputStream: 바이트 단위 입력 스트림의 최상위 추상 클래스
      • FileInputStream: 파일에서 바이트 데이터를 읽어오는 클래스
      • ByteArrayInputStream: 메모리에 저장된 바이트 배열에서 데이터를 읽어오는 클래스
      • ObjectInputStream: 객체를 역직렬화하여 바이트 스트림으로 읽어오는 클래스
      • FilterInputStream: 다른 입력 스트림에 기능을 추가하는 데 사용되는 클래스 (예: BufferedInputStream)
    • OutputStream: 바이트 단위 출력 스트림의 최상위 추상 클래스
      • FileOutputStream: 파일에 바이트 데이터를 쓰는 클래스
      • ByteArrayOutputStream: 메모리에 바이트 데이터를 쓰는 클래스
      • ObjectOutputStream: 객체를 바이트 스트림으로 쓰는 클래스
      • FilterOutputStream: 다른 출력 스트림에 기능을 추가하는 데 사용되는 클래스 (예: BufferedOutputStream)

BufferedInputStream: 입력/출력 스트림의 성능을 향상하기 위해 버퍼를 사용하는 클래스

2. 문자 스트림 (Character Stream)

  • 데이터 단위: 2바이트 (16비트) 단위로 데이터를 처리합니다. 유니코드를 기반으로 문자를 처리하기 때문에 다양한 언어를 지원합니다.
  • 용도: 텍스트 파일, 웹 페이지 등 문자 데이터를 처리하는 데 적합합니다.
  • 주요 클래스:
    • Reader: 문자 단위 입력 스트림의 최상위 추상 클래스
      • FileReader: 파일에서 문자 데이터를 읽어오는 클래스
      • CharArrayReader: 메모리에 저장된 문자 배열에서 데이터를 읽어오는 클래스
      • StringReader: 문자열에서 데이터를 읽어오는 클래스
      • InputStreamReader: 바이트 스트림을 문자 스트림으로 변환하는 클래스 (인코딩 지원)
    • Writer: 문자 단위 출력 스트림의 최상위 추상 클래스
      • FileWriter: 파일에 문자 데이터를 쓰는 클래스
      • CharArrayWriter: 메모리에 문자 데이터를 쓰는 클래스
      • StringWriter: 문자열에 데이터를 쓰는 클래스
      • OutputStreamWriter: 문자 스트림을 바이트 스트림으로 변환하는 클래스 (인코딩 지원)

바이트 스트림 vs 문자 스트림

특징 바이트 스트림 문자 스트림
데이터 단위 1바이트 2바이트
용도 이진 데이터 문자 데이터
인코딩 필요 없음 필요 (UTF-8, UTF-16 등)
클래스 InputStream, OutputStream Reader, Writer

주의 사항

  • 바이트 스트림으로 문자 데이터를 처리하면 인코딩 문제가 발생할 수 있습니다. 따라서 문자 데이터는 문자 스트림으로 처리하는 것이 안전합니다.
  • InputStreamReaderOutputStreamWriter를 사용하여 바이트 스트림과 문자 스트림을 상호 변환할 수 있습니다. 이때 변환할 인코딩을 지정할 수 있습니다.

Java

InputStreamReader reader = new InputStreamReader(new FileInputStream("text.txt"), "UTF-8");
OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream("output.txt"), "UTF-8");

예제

Java

// 바이트 스트림으로 이미지 파일 복사
InputStream in = new FileInputStream("image.jpg");
OutputStream out = new FileOutputStream("image_copy.jpg");
byte[] buffer = new byte[1024];
int length;
while ((length = in.read(buffer)) > 0) {
    out.write(buffer, 0, length);
}
in.close();
out.close();

// 문자 스트림으로 텍스트 파일 읽기
Reader reader = new FileReader("text.txt");
BufferedReader br = new BufferedReader(reader);
String line;
while ((line = br.readLine()) != null) {
    System.out.println(line);
}
br.close();
rea1der.close();

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

Java에서 표준 스트림은 프로그램과 외부 환경(주로 콘솔) 사이의 데이터 입출력을 담당하는 기본적인 스트림입니다. System 클래스에서 제공하며, System.in, System.out, System.err 이렇게 세 가지가 있습니다.

1. System.in (표준 입력 스트림)

  • 역할: 키보드 등의 입력 장치에서 데이터를 읽어오는 역할을 합니다.
  • 타입: InputStream
  • 사용 예시:
    • System.in.read() 메서드를 사용하여 키보드로부터 한 바이트씩 데이터를 읽어올 수 있습니다.
    • Scanner 클래스를 이용하여 사용자 입력을 편리하게 받을 수 있습니다.

Java

import java.util.Scanner;

public class StandardInputExample {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("이름을 입력하세요: ");
        String name = scanner.nextLine();
        System.out.println("입력하신 이름은 " + name + "입니다.");
    }
}

2. System.out (표준 출력 스트림)

  • 역할: 콘솔 화면에 데이터를 출력하는 역할을 합니다.
  • 타입: PrintStream
  • 사용 예시:
    • System.out.println() 메서드를 사용하여 콘솔에 문자열을 출력할 수 있습니다.
    • System.out.print() 메서드는 줄 바꿈 없이 문자열을 출력합니다.
    • System.out.printf() 메서드는 서식 문자열을 사용하여 출력 형식을 지정할 수 있습니다.

Java

public class StandardOutputExample {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
        System.out.print("이름: ");
        System.out.println("홍길동");
        System.out.printf("나이: %d세\n", 20);
    }
}

3. System.err (표준 에러 스트림)

  • 역할: 콘솔 화면에 오류 메시지를 출력하는 역할을 합니다.
  • 타입: PrintStream
  • 사용 예시:
    • System.err.println() 메서드를 사용하여 콘솔에 오류 메시지를 출력할 수 있습니다. 주로 프로그램 실행 중 예외 상황이나 오류 정보를 출력하는 데 사용됩니다.

Java

public class StandardErrorExample {
    public static void main(String[] args) {
        try {
            int result = 10 / 0;
        } catch (ArithmeticException e) {
            System.err.println("0으로 나눌 수 없습니다: " + e.getMessage());
        }
    }
}

특징

  • 기본적으로 콘솔과 연결: 표준 스트림은 기본적으로 콘솔(터미널)과 연결되어 있습니다. 즉, System.in은 키보드 입력을 받고, System.outSystem.err은 콘솔 화면에 출력합니다.
  • 리디렉션 가능: 운영체제의 리디렉션 기능을 사용하여 표준 스트림의 입력 소스와 출력 대상을 변경할 수 있습니다. 예를 들어, System.out의 출력을 파일로 리디렉션 하거나, System.in의 입력을 파일에서 읽어올 수 있습니다.
    • 리디렉션 예제
      java MyProgram > output.txt  # System.out을 output.txt로 리디렉션
      java MyProgram < input.txt   # System.in을 input.txt로 리디렉션
  • System.setIn(), System.setOut(), System.setErr() 메서드를 사용하여 표준 스트림을 다른 스트림으로 변경할 수 있습니다.

파일 읽고 쓰기

Java에서 파일을 읽고 쓰는 방법은 다양하지만, 기본적으로 스트림(Stream)을 사용합니다. 파일을 읽을 때는 InputStream을, 파일에 쓸 때는 OutputStream을 사용하며, 바이트 스트림과 문자 스트림을 선택하여 사용할 수 있습니다.

1. 바이트 스트림으로 파일 읽고 쓰기

파일 읽기:

  • FileInputStream 클래스를 사용하여 파일에서 바이트 단위로 데이터를 읽어옵니다.
  • read() 메서드를 사용하여 바이트 단위로 데이터를 읽거나, read(byte [] b) 메서드를 사용하여 바이트 배열에 데이터를 읽어올 수 있습니다.

Java

import java.io.FileInputStream;
import java.io.IOException;

public class FileReadExample {
    public static void main(String[] args) throws IOException {
        FileInputStream 1fis = new FileInputStream("input.txt");
        int data;
        while ((data = fis.read()) != -1) {
            System.out.print((char) data);
        }
        fis.close();
    }
}

파일 쓰기:

  • FileOutputStream 클래스를 사용하여 파일에 바이트 단위로 데이터를 씁니다.
  • write() 메서드를 사용하여 바이트 단위로 데이터를 쓰거나, write(byte [] b) 메서드를 사용하여 바이트 배열에 있는 데이터를 쓸 수 있습니다.

Java

import java.io.FileOutputStream;
import java.io.IOException;

public class FileWriteExample {
    public static void main(String[] args) throws IOE2xception {
        FileOutputStream fos = new FileOutputStream("3output.txt");
        String data = "Hello, World!";
        fos.write(data.getBytes());
        fos.close();
    }
}

2. 문자 스트림으로 파일 읽고 쓰기

파일 읽기:

  • FileReader 클래스를 사용하여 파일에서 문자 단위로 데이터를 읽어옵니다.
  • read() 메서드를 사용하여 문자 단위로 데이터를 읽거나, read(char [] cbuf) 메서드를 사용하여 문자 배열에 데이터를 읽어올 수 있습니다.

Java

import java.io.FileReader;
import java.io.IOException;

public class FileReadExample {
    public static void main(String[] args) throws IOExceptio4n {
        FileReader fr = new FileReader("input.txt");
        int data;
        while ((data = fr.read()) != -1) {
            System.out.print((char) data);
        }
        fr.close();
    }
}

파일 쓰기:

  • FileWriter 클래스를 사용하여 파일에 문자 단위로 데이터를 씁니다.
  • write() 메서드를 사용하여 문자 단위로 데이터를 쓰거나, write(char [] cbuf) 메서드를 사용하여 문자 배열에 있는 데이터를 쓸 수 있습니다.

Java

import java.io.FileWriter;
import java.io.IOException;

public class FileWriteExample {
    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileW5riter("output.txt");
        String data = "Hello, World!";
        fw.write(data);
        fw.close();
    }
}

3. BufferedReader & BufferedWriter

  • BufferedReaderBufferedWriter는 버퍼를 사용하여 파일 읽기/쓰기 속도를 향상하는 클래스입니다.
  • BufferedReaderreadLine() 메서드를 제공하여 한 줄씩 읽어올 수 있습니다.
  • BufferedWriternewLine() 메서드를 제공하여 줄 바꿈을 추가할 수 있습니다.

Java

import java.io.*;

public class BufferedReadWriteExample {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("input.txt"));
        BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"));
        String line;
        while ((line = br.readLine()) != null) {
            bw.write(line);
            bw.newLine();
        }
        br.close();
        bw.cl6ose();
    }
}

4. try-with-resources

  • Java 7부터 try-with-resources 문을 사용하여 스트림을 자동으로 닫을 수 있습니다.
  • try-with-resources 문을 사용하면 finally 블록에서 close() 메서드를 호출하지 않아도 됩니다.

Java

import java.io.*;

public class TryWithResourcesExample {
    public static void main(String[] args) throws IOException {
        try (BufferedReader br = new BufferedReader(new FileReader("input.txt"));
             BufferedWriter bw = new BufferedWrite7r(new FileWriter("output.txt"))) {
            String line;
            while ((line = br.readLine()) != null) {
                bw.write(line);
                bw.8newLine();
            }
        }
    }
}

추가 정보

  • 파일 경로는 절대 경로 또는 상대 경로로 지정할 수 있습니다.
  • 파일이 존재하지 않으면 FileNotFoundException 예외가 발생합니다.
  • 파일 입출력 작업 중에는 IOException 예외가 발생할 수 있습니다.
  • File 클래스를 사용하여 파일의 존재 여부, 크기, 수정 날짜 등의 정보를 얻을 수 있습니다.

'코딩스터디 > JAVA스터디' 카테고리의 다른 글

Java 람다식  (0) 2024.11.21
Java 제네릭  (1) 2024.11.20
Java 애노테이션  (3) 2024.11.18
Java) Enum  (3) 2024.11.17
Java 멀티쓰레드 프로그래밍  (2) 2024.11.16