복잡한뇌구조마냥

[JAVA] 자바 IO ( Byte 단위, 다양한 타입, Char 단위 입출력) 본문

BE/JAVA

[JAVA] 자바 IO ( Byte 단위, 다양한 타입, Char 단위 입출력)

지금해냥 2025. 6. 14. 20:48

자바 IO (Input + Output)

- 프로그램 상에서 들어오는 데이터를 Input 데이터

- 프로그램 상에서 나가는 데이터를 Output 데이터

  • byte 단위 입출력 클래스는 모두 InputStream과 OutputStream이라는 추상클래스를 상속받아 만들어짐.
  • 문자(char) 단위 입출력 클래스는 모두 Reader와 Writer라는 추상클래스를 상속받아 만들어짐
  • 4가지 추상클래스를 받아들이는 생성자가 있다면, 다양한 입출력방법을 제공하는 클래스임.
  • 4가지 클래스를 받아들이는 생성자가 없다면, ①어디로부터 입력받을 것인지, ②어디에 쓸것인지를 나타내는 클래스

  • 파일로부터 입력받고 쓰기 위한 클래스 : FileInputStream, FileOutputStream, FileReader, FileWriter
  • 배열로부터 입력받고 쓰기 위한 클래스 : ByteArrayInputStream, ByteArrayOutputStream, CharReader, CharWriter
    • 해당 클래스들은 어디로부터, 어디에 라는 대상을 지정할 수 있는 IO 클래스
    • 장식대상 클래스
  • DataInputStream, DataOutputStream 같은 클래스를 보면 다양한 데이터 형을 입력받고 출력함.
  • PrintWriter는 다양한 한줄 출력하는 println() 메소드를 가지고 있음
  • BufferedReader는 한줄 입력받는 readLine()메소드를 가짐
    • 다양한 방식으로 입력하고, 출력하는 기능을 제공
    • 장식하는 클래스

Byte 단위 입출력

- Byte 단위 입출력 클래스는 클래스의 이름이 InputStream이나 OutputStream으로 끝남.

  • 파일로 부터 1byte씩 읽어들여 파일에 1byte씩 저장하는 프로그램 작성
    • 파일로 부터 읽어오기 위한 객체 - FileInputStream
    • 파일에 쓸 수 있게 해주는 객체 - FileOutputStream
  • read() 메소드
    • byte를 리턴한다면 끝에 나타내는 값을 표현할 수 없기 때문에, byte가 아닌 int를 return함
    • 음수의 경우 맨 좌측 비트가 1이 됨.
    • 읽어들일 것이 있다면 항상 양수를 return  함
  • FileInputStream과 FileOutputStream을 이용하여 1바이트씩 읽고 1바이트씩 쓰기
    • read() 메소드가 return 하는 타입은 정수, 정수 4바이트중 마지막 바이트에 읽어들인 1바이트를 저장.
    • read() 메소드는 더이상 읽어들일 것이 없을 때, -1을 return 함
// src/javaIO/exam/ByteExam1.java
package javaIO.exam;

import java.io.FileInputStream;
import java.io.FileOutputStream;

public class ByteExam1 {
    public static void main(String[] args) {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            fis = new FileInputStream("src/javaIO/exam/ByteExam1.java");
            fos = new FileOutputStream("byte.txt");

            int readData = -1;
            while((readData = fis.read()) != -1){
                fos.write(readData);
            }
        }catch(Exception e){
            e.printStackTrace();
        } finally {
            try {
                fos.close();
            } catch (Exception e) {
                e.printStackTrace();
            } try{
                fis.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

- 현재 파일을 읽어들여서 특정 파일에 값을 복사하는 코드

- ByteExam1.java파일을 실행했더니 byte.txt 파일에 ByteExam1.java파일의 내용과 동일한 내용을 복사함.

- FileInputStream과 FileOutputStream은 Exception을 통해서 예외처리가 필요함

- 사용이 끝나면 close() 메소드를 통해서 끝내줘야함. close 메소드도 Exception 처리 필요.

 

byte 배열을 이용한 입출력

// src/javaIO/exam/ByteExam2.java
package javaIO.exam;

import java.io.FileInputStream;
import java.io.FileOutputStream;

public class ByteExam2 {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            fis = new FileInputStream("src/javaIO/exam/ByteExam1.java");
            fos = new FileOutputStream("byte.txt");

            int readCount = -1;
            byte[] buffer = new byte[512];
            while((readCount = fis.read(buffer)) != -1){
                fos.write(buffer, 0, readCount);
            }
        }catch(Exception e){
            e.printStackTrace();
        } finally {
            try {
                fos.close();
            } catch (Exception e) {
                e.printStackTrace();
            } try{
                fis.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        long end = System.currentTimeMillis();
        System.out.println(end-start);
    }
}

- 파일로부터 512 byte씩 읽어서 buffer 라는 배열에 저장하는 방식을 이용해서 512 byte씩 입출력

- int를 이용해서 1byte씩 읽는 것과 속도차이가 많이나는 것을 확인할 수 있음. ( Exam1에서는 38, Exam2에서는 3)

 

512바이트씩 읽는 방법이 빠른 이유

- OS에서 파일을 읽을 때, 512 byte씩 읽어오도록 되어 있음.

- 1byte씩 읽는 방식은 512 byte를 읽어와서 1byte를 제외한 나머지 511byte를 버림 (비효율적)

- 따라서 512의 배수로 배열을 잡아주는 것이 파일을 읽고 쓰기에 성능상 유리함.

 

다양한 타입의 입출력

try-with-resources

- 사용한 자원을 자동으로 종료시켜주는 기능

- java io객체는 인스턴스를 만들고, 모두 사용하면 close()메소드를 호출해야함

- 사용자가 호출하지 않더라도, Exception이 발생하지 않았다면 자동으로 close()가 되는 방법임.

try(
	// io객체 선언
){
	// io객체 사용
}catch(Exception ex){
	// 예외처리
}

 

 

다양한 타입의 입출력

출력

package javaIO.exam;

import java.io.DataOutputStream;
import java.io.FileOutputStream;

public class ByteExam3 {
    public static void main(String[] args) {
        try(
                DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
                ) {
            dos.writeInt(100);
            dos.writeBoolean(true);
            dos.writeDouble(50.5);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  • DataOutputStream을 이용해서 다양한 타입을 저장
  • writeInt() - 정수값 저장
  • wirteBoolean() - boolean 값 저장
  • wirteDouble() - double 값 저장

  • 데이터 값이 저장된 것이기 때문에 숫자 나 문자로 표시된것이 아님.
  • 4 byte (int) + 1 byte (boolean) + 8 byte (double) = 13 byte

입력

  • DataInputStream을 이용해서 다양한 타입을 저장
  • readInt() - 정수값 불러오기
  • readBoolean() - boolean 값 불러오기
  • readDouble() - double 값 불러오기
package javaIO.exam;

import java.io.DataInputStream;
import java.io.FileInputStream;

public class ByteExam4 {
    public static void main(String[] args) {
        try(
                DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
        ) {
            int i = dis.readInt();
            boolean b = dis.readBoolean();
            double d = dis.readDouble();

            System.out.println(i);
            System.out.println(b);
            System.out.println(d);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

  • 만들어진 파일을 읽어서 다양한 타입의 값을 사용할 수 있음.
  • 파일에 저장된 순서대로 읽어야함.
    • int, boolean, double 순서대로 저장했기 때문에 읽을 때도 동일한 순서로 읽어야함.

 

Char 단위 입출력

  • Char 단위 입출력 클래스는 클래스 이름이 Reader나 writer로 끝남

Console

  • char 단위 입출력 클래스를 이용해서 키보드로부터 한줄 입력을 받아서 콘솔에 출력
    • System.in - 키보드를 의미 ( InputStream )
    • BufferedReader - 한줄씩 입력 받기 위한 클래스
    • BufferedReader 클래스의 생성자는 InputStream을 입력받는 생성자가 없음
    • System.in은 InputStream 타입이므로 BufferedReader의 생성자에 바로 들어갈 수 없으므로 InputStreamReader 클래스를 이용
package javaIO.exam;

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class CharIOExam1 {
    public static void main(String[] args) {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        try {
            String line = br.readLine();
            System.out.println(line);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

  • 한줄 입력받아서 line이라는 변수에 넣고 console 로 출력하는 방식
  • System.in 을 생성자에 사용할 때 쓴 방식은 데코레이터 패턴이라고 볼 수 있음.
  • 데코레이터 패턴을 이용해서 키보드 입력이 아닌 파일 입력도 가능함.
  • readLine 문법을 할 때, try-catch 구문 사용 필요.

* 데코레이터 패턴 (Decorator Pattern)

- 객체에 추가적인 요건(기능)을 동적으로 첨가하는 방식

- 서브클래스를 만드는 것을 통하여 기능을 유연하게 확장할 수 있는 방법 제공

 

File

  • 파일에서 한 줄씩 입력 받아서 파일에 출력
    • 파일에서 읽기 위해서 FileReader 클래스 이용
    • 한 줄 읽어들이기 위해서 BufferedReader 클래스 이용
      • BuffedReader 클래스가 가지고 있는 readLine() 메소드가 한줄씩 읽게 해줌.
      • readLine() 메소드는 읽어낼 때 더 이상 읽어들일 내용이 없으면 null을 return함
    • 파일에 쓰게하기 위해서 FileWriter 클래스 이용
    • 편리하게 출력하기 위해서 PrintWriter 클래스 이용
      • 출력할 때 많이 사용하는 System.out도 PrintWriter 객체임
package javaIO.exam;

import java.io.*;

public class CharIOExam2 {
    public static void main(String[] args) {
        BufferedReader br = null;
        PrintWriter pw = null;
        try{
            br = new BufferedReader(new FileReader("src/javaIO/exam/CharIOExam2.java"));
            // 데코레이터 패턴으로 굳이 사용하지 않아도 사용할 수 있음.
            pw = new PrintWriter(new FileWriter("test.txt"));

            String line = null;
            while((line = br.readLine()) != null){
                pw.println(line);
            }
        }  catch (Exception e) {
            e.printStackTrace();
        } finally {
            pw.close();
            try{
                br.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

- close() 메소드를 이용해서 항상 종료시켜줘야함.

- test.txt파일에 내용이 복사된 것을 확인 할 수 있음.

LIST