자바 개발자 일기 20일차 (InputStream, OutputStream)

Shy·2024년 1월 24일

아이티윌

목록 보기
20/22

자바의 입출력

자바에서 입출력(I/O)은 데이터를 읽고 쓰는 과정을 말한다. 이는 파일, 네트워크, 메모리 등 다양한 소스와 목적지에 대해 수행될 수 있다.

자바의 입출력에 대한 특징은 다음과 같다.

  1. 스트림(Stream) 기반
    • 자바에서 데이터는 스트림을 통해 이동합니다. 스트림은 데이터의 연속적인 흐름을 의미하며, 입력 스트림과 출력 스트림으로 구분됩니다.
  2. 바이트 스트림과 문자 스트림
    • 바이트 스트림: 모든 종류의 데이터(텍스트, 이미지, 비디오 등)를 8비트 바이트 단위로 처리한다. InputStreamOutputStream이 바이트 스트림의 기본 클래스이다.
    • 문자 스트림: 문자 데이터를 처리할 때 사용된다. 문자 스트림은 유니코드 문자를 처리하기 때문에 국제화된 문자를 쉽게 다룰 수 있다. ReaderWriter가 문자 스트림의 기본 클래스이다.
    • 버퍼링(Buffering): 성능 향상을 위해 버퍼링을 사용할 수 있다. 버퍼링은 데이터를 메모리의 버퍼에 임시로 저장했다가 일정량이 모이면 한 번에 처리한다. BufferedReader, BufferedWriter, BufferedInputStream, BufferedOutputStream 등이 이에 해당한다.
  3. 파일 I/O
    • 자바에서는 File, FileInputStream, FileOutputStream, FileReader, FileWriter 등을 통해 파일 입출력을 수행한다.
  4. 직렬화(Serialization)
    • 객체의 상태를 바이트 스트림으로 변환하여 파일이나 네트워크를 통해 전송할 수 있다. ObjectInputStreamObjectOutputStream이 이 기능을 제공한다.
  5. 네트워크 I/O: SocketServerSocket 클래스를 사용하여 네트워크를 통한 데이터 전송을 수행할 수 있다.
  6. NIO(New Input/Output)
    • 자바의 NIO 패키지는 비동기 입출력을 지원하며, 채널(Channel)과 버퍼(Buffer)를 사용해 높은 성능의 입출력 처리를 가능하게 한다.

자바의 입출력 시스템은 다양하고 강력하여 여러 용도로 활용될 수 있다. 각각의 클래스와 메소드는 특정한 유형의 데이터 처리에 최적화되어 있으므로, 상황에 따라 적절한 도구를 선택하여 사용하는 것이 중요하다.

바이트 스트림, 문자 스트림

바이트 스트림

InputStreamOutputStream 클래스가 바이트 스트림의 기반을 이룬다. 이들은 바이너리 데이터를 처리하기에 적합하다. 예를 들어, 이미지나 오디오 파일과 같은 비텍스트 데이터를 읽고 쓸 때 주로 사용된다. 예시로 FileInputStreamFileOutputStream이 있다.

예시코드

InputStream is = new FileInputStream("example.txt");
int byteData;
while ((byteData = is.read()) != -1) {
    // 데이터 처리
}
is.close();

InputStream

인풋 스트림(Input Stream)은 자바에서 데이터를 읽어들이는 데 사용되는 스트림의 한 종류다. 인풋 스트림은 데이터 소스로부터 바이트 단위로 데이터를 읽는 기능을 제공한다. 주로 파일, 메모리, 네트워크 연결 등 다양한 데이터 소스로부터 데이터를 읽을 때 사용한다. InputStream 클래스는 자바에서 인풋 스트림의 기본이 되는 추상 클래스다.

주요 특징
  • 바이트 기반 스트림: 인풋 스트림은 바이트 단위로 데이터를 읽기 때문에 텍스트뿐만 아니라 이미지, 비디오 파일 등의 바이너리 데이터도 읽을 수 있다.
  • 블로킹 I/O: 대부분의 인풋 스트림은 데이터가 사용 가능할 때까지 읽기 작업을 블록한다. 즉, 데이터를 기다리는 동안 다른 작업을 수행하지 않고 대기한다.
  • 버퍼링: 성능 향상을 위해 버퍼링을 사용할 수 있다. 예를 들어, BufferedInputStream은 내부 버퍼를 사용하여 한 번에 여러 바이트를 읽어들임으로써 입출력 효율을 개선한다.
InputStream inputStream = new FileInputStream("example.txt");
int data = inputStream.read();
while(data != -1) {
    // 데이터 처리
    data = inputStream.read();
}
inputStream.close();

OutputStream

아웃풋 스트림(Output Stream)은 자바에서 데이터를 쓰는 데 사용되는 스트림의 한 종류이다. 이 스트림은 데이터를 바이트 단위로 외부로 출력하는 기능을 제공한다. 주로 파일, 메모리, 네트워크 연결 등과 같은 목적지에 데이터를 쓸 때 사용된다. OutputStream 클래스는 자바에서 아웃풋 스트림의 기본이 되는 추상 클래스다.

주요 특징
  • 바이트 기반 스트림: 아웃풋 스트림은 바이트 단위로 데이터를 쓴다. 이로 인해 텍스트뿐만 아니라 이미지, 비디오 파일 등의 바이너리 데이터도 쓸 수 있다.
  • 버퍼링: 성능 향상을 위해 버퍼링을 사용할 수 있다. 예를 들어, BufferedOutputStream은 내부 버퍼를 사용하여 한 번에 여러 바이트를 쓰도록 함으로써 입출력 효율을 개선한다.
  • 데이터 플러시: 쓰기 작업을 마친 후에는 flush() 메소드를 호출하여 버퍼에 남아 있는 모든 데이터를 강제로 쓰게 할 수 있다. 이는 데이터 손실을 방지하는 데 중요하다.
OutputStream outputStream = new FileOutputStream("example.txt");
outputStream.write(data);
// 필요한 모든 데이터 쓰기
outputStream.flush();
outputStream.close();

FileOutputStream은 파일에 데이터를 쓰는 아웃풋 스트림의 한 예시다. write() 메소드는 데이터를 한 바이트씩 쓴다. flush() 메소드는 스트림의 버퍼에 남아 있는 모든 데이터를 출력한다. 마지막으로 스트림을 닫아 리소스를 해제한다.

아웃풋 스트림을 사용할 때는 데이터 쓰기를 마친 후에 항상 스트림을 닫아주는 것이 중요하다. 이는 리소스 누수를 방지하고 시스템의 안정성을 유지하는 데 도움이 된다. Java 7부터는 try-with-resources 문을 사용하여 스트림을 자동으로 닫을 수 있다.

문자 스트림

ReaderWriter 클래스가 문자 스트림의 기반을 이룬다. 이들은 텍스트 데이터를 처리하는 데 최적화되어 있다. 문자 스트림은 유니코드 문자를 적절히 인코딩하고 디코딩하여, 국제화가 중요한 어플리케이션에서 유용하다. 예시로 FileReaderFileWriter가 있다.

Reader reader = new FileReader("example.txt");
int charData;
while ((charData = reader.read()) != -1) {
    // 텍스트 처리
}
reader.close();

바이트 스트림과 문자 스트림 중 어떤 것을 사용할지는 처리하려는 데이터의 종류에 따라 결정된다. 바이너리 데이터를 다룰 때는 바이트 스트림을, 텍스트 데이터를 다룰 때는 문자 스트림을 사용하는 것이 바람직하다.

(FileInputStream, FileOutputStream) / (FileReader, FileWriter)

  1. FileReader와 FileWriter (문자 스트림)
    • 문자 지향: 이들은 텍스트 데이터를 처리하기 위해 설계되었다.
    • 문자 단위 처리: FileReader와 FileWriter는 유니코드 문자를 16비트 단위로 읽고 쓴다. 이는 텍스트 파일의 읽기와 쓰기에 최적화되어 있다.
    • 문자 인코딩 처리: 문자 인코딩을 자동으로 처리한다. 예를 들어, 한글과 같은 다국어 문자를 올바르게 읽고 쓸 수 있다.
    • 사용 예시: 텍스트 파일을 읽거나 쓸 때 주로 사용된다.
  2. FileInputStream과 FileOutputStream (바이트 스트림)
    • 바이트 지향: 이들은 모든 종류의 데이터(텍스트, 이미지, 비디오 등)를 처리할 수 있도록 설계되었다.
    • 바이트 단위 처리: FileInputStream과 FileOutputStream은 데이터를 8비트 단위의 바이트로 읽고 씁니다. 이는 모든 유형의 파일을 처리하는 데 적합하다.
    • 인코딩 처리 없음: 바이트 스트림은 인코딩 처리를 하지 않기 때문에, 문자 데이터를 다룰 때는 인코딩을 직접 관리해야 할 수도 있다.
    • 사용 예시: 바이너리 파일(이미지, 오디오, 비디오 파일 등)을 처리하거나, 텍스트 데이터에서 특별한 인코딩이 필요한 경우에 사용된다.

요약

FileReaderFileWriter는 텍스트 데이터를 위한 문자 스트림으로, 문자 인코딩을 자동으로 처리한다. 이는 주로 텍스트 파일을 읽고 쓰는 데 사용된다.
FileInputStreamFileOutputStream은 모든 유형의 데이터를 처리할 수 있는 바이트 스트림으로, 데이터를 바이트 단위로 처리한다. 바이너리 파일을 다루거나 특별한 인코딩이 필요한 경우에 주로 사용된다.

예제코드 (FileReader)

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class FileReader_Exam1 {

    public static void main(String[] args) {
        // 문자(character)단위 입력처리
        // FileReader : 파일로 부터 문자 단위 데이터 읽기
        // 문자단위 : 1, A, $와 같은 문자는 1byte 문자이다.(UTF-8기준), (또는 2byte)
        //          가, 대, 한 - 3byte(또는 2byte) 문자(한글, 아랍어, 중국어, 태국어 일본어 등등등... 알파벳 계열이 아닌 것들)
        // -------------------------------
        // 'file/test_char.txt' 파일에 대한 File 객체를 생성한다.
        File file = new File("file/test_char.txt");
        // 파일의 크기를 바이트 단위로 출력한다.
        System.out.println("file.length() : " + file.length() + "byte");

        // FileReader 객체를 선언한다.
        FileReader fr = null;

        try {
            //1. FileReader 객체(인스턴스)를 생성한다.
            fr = new FileReader(file);

            // 2. FileReader 객체를 사용하여 파일로부터 문자를 읽고, 화면에 출력한다.
            // 첫 번째 문자를 읽고 출력한다.
            int readChar = fr.read();
            System.out.println("첫 번째 문자 : " + readChar + ", char : " + (char)readChar);

            // 두 번째 문자를 읽고 출력한다.
            readChar = fr.read();
            System.out.println("두 번째 문자 : " + readChar + ", char : " + (char)readChar);

            // 세 번째 문자를 읽고 출력한다.
            readChar = fr.read();
            System.out.println("세 번째 문자 : " + readChar + ", char : " + (char)readChar);

            // 네 번째 문자를 읽고 출력한다.
            readChar = fr.read();
            System.out.println("네 번째 문자 : " + readChar + ", char : " + (char)readChar);

        } catch (FileNotFoundException e) {
            // 파일이 없을 경우 예외 처리
            e.printStackTrace();
        } catch (IOException e) {
            // 입출력 처리 중 오류가 발생한 경우 예외 처리
            e.printStackTrace();
        } finally {
            // 3. FileReader 객체를 닫는다. 이는 중요한 자원 해제 과정이다.
            try {
                if (fr != null) fr.close();
            } catch (IOException e) {
                // 객체 닫기 중 예외 발생 시 처리
                e.printStackTrace();
            }
        }
    }
}
file.length() : 39byte
첫 번째 문자 : 65, char : A
두 번째 문자 : 44032, char : 가
세 번째 문자 : 44033, char : 각
네 번째 문자 : 45824, char : 대

예제코드 (FileReader, 반복문)

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class FileReader_Exam2 {

    public static void main(String[] args) {
        // 'file/test_char.txt' 파일에 대한 File 객체를 생성한다.
        File file = new File("file/test_char.txt");
        // 파일의 크기를 바이트 단위로 출력한다.
        System.out.println("file.length() : " + file.length() + "byte");
        
        // FileReader 객체를 선언한다.
        FileReader fr = null;
        
        try {
            //1. FileReader 객체(인스턴스)를 생성한다.
            fr = new FileReader(file);
            
            // 2. 파일의 끝에 도달할 때까지 반복하여 문자를 읽고 화면에 출력한다.
            int readChar = fr.read();
            while (readChar != -1) {
                // 읽은 문자를 출력한다. 여기서 char 타입으로 형변환을 해서 문자를 출력한다.
                System.out.print((char)readChar);
                // 다음 문자를 읽는다.
                readChar = fr.read();
            }
            
        } catch (FileNotFoundException e) {
            // 파일이 존재하지 않을 때의 예외 처리
            e.printStackTrace();
        } catch (IOException e) {
            // 입출력 중 발생하는 예외 처리
            e.printStackTrace();
        } finally {
            // 3. FileReader 객체를 닫는다.
            try {
                if (fr != null) fr.close();
            } catch (IOException e) {
                // 객체 닫기 과정에서 발생하는 예외 처리
                e.printStackTrace();
            }
        }
    }
}
file.length() : 39byte
A가각대한민국
123456789
!@#$%^&*(

예제코드 (FileWriter)

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

public class FileWriter_Exam {

    public static void main(String[] args) {
        // FileWriter를 사용하여 파일에 문자 데이터를 쓰는 예제
        // 'file/test_char_out.txt' 파일에 대한 File 객체를 생성한다.
        File file = new File("file/test_char_out.txt");
        
        // FileWriter 객체를 선언한다.
        FileWriter fw = null;
        
        try {
            // 1. FileWriter 객체(인스턴스)를 생성한다.
            // 이 때 지정된 파일에 데이터를 쓸 준비를 한다.
            fw = new FileWriter(file);
            
            // 2. FileWriter 객체를 사용하여 파일에 문자 데이터를 쓴다.
            // 한글 '대'를 파일에 쓴다.
            fw.write('대');
            // 한글 '한'을 파일에 쓴다.
            fw.write('한');
            // 문자열 "민국"을 파일에 쓴다.
            fw.write("민국");
            
            // fw.flush() 호출: 버퍼에 있는 모든 데이터를 파일에 즉시 쓴다.
            // 버퍼가 다 차지 않아도 데이터를 강제로 파일에 출력한다.
            fw.flush();
            
        } catch (IOException e) {
            // 입출력 과정에서 발생하는 예외 처리
            e.printStackTrace();
        } finally {
            // 3. FileWriter 객체를 닫는다.
            // 이 과정은 자원 해제 및 데이터 무결성 보장을 위해 중요하다.
            try {
                if (fw != null) fw.close();
            } catch (IOException e) {
                // 객체 닫기 과정에서 발생하는 예외 처리
                e.printStackTrace();
            }
        }
    }
}

예제코드 (FileCopy)

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.FileNotFoundException;
import java.io.IOException;

public class FileCopyCharacterExam {

    public static void main(String[] args) {
        // FileReader와 FileWriter를 사용하여 파일을 복사하는 예제
        // 원본 파일: 'file/test_char.txt'
        // 대상 파일: 'file/test_char_copy.txt'
        // 처리: 원본 파일을 읽어 대상 파일에 문자 단위로 쓰기/저장/출력

        // FileInputStream과 FileOutputStream을 사용할 경우 한글이 깨질 수 있으므로 
        // 문자 처리에 적합한 FileReader와 FileWriter를 사용한다.

        // 원본 파일과 대상 파일에 대한 File 객체를 생성한다.
        File inputFile = new File("file/test_char.txt");
        File outputFile = new File("file/test_char_copy.txt");
        
        // FileReader와 FileWriter 객체를 선언한다.
        FileReader fis = null;
        FileWriter fos = null;
        
        try {
            // 원본 파일을 읽기 위한 FileReader 객체를 생성한다.
            fis = new FileReader(inputFile);
            // 대상 파일을 쓰기 위한 FileWriter 객체를 생성한다.
            fos = new FileWriter(outputFile);
            
            // 파일의 끝에 도달할 때까지 한 문자씩 읽는다.
            int readValue = fis.read();
            
            while(readValue != -1) {
                // 읽은 문자를 대상 파일에 쓴다.
                fos.write(readValue);
                // 읽은 문자를 콘솔에 출력한다.
                System.out.print((char)readValue);
                // 다음 문자를 읽는다.
                readValue = fis.read();
            }
            
        } catch (FileNotFoundException e) {
            // 파일이 존재하지 않는 경우의 예외 처리
            e.printStackTrace();
        } catch (IOException e) {
            // 입출력 중 발생하는 예외 처리
            e.printStackTrace();
        } finally {
            // FileReader 객체를 닫는다.
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            // FileWriter 객체를 닫는다.
            try {
                if (fos != null) fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
A가각대한민국
123456789
!@#$%^&*(

버퍼

profile
신입사원...

0개의 댓글