본문 바로가기

Java/기본 개념

입출력 (IO_Input / Output)

▶ 입출력 (IO)이란?

  • 컴퓨터 내부 또는 외부 장치와 프로그램 간의 데이터를 주고 받는 것
  • 다양한 매체에 존재하는 데이터들을 사용하기 위해 입출력 데이터를 처리할 공통적인 방법으로 스트림(Stream) 이용
  • Input(입력) : 외부 → 내부로 데이터를 들여오는 것
  • Output(출력) : 내부 → 외부로 데이터를 내보내는 것
  • Stream(스트림) : 데이터가 이동하는 통로 (기본적으로 단 방향)

▶ File 클래스

  • 파일 시스템의 파일을 표현하는 클래스
  • 파일 크기, 속성, 파일 이름 등의 정보와 파일 생성 및 삭제 기능 제공
File file = new File("C:/io/test.txt")

▶ 스트림 (Stream) 클래스

  • 입출력 장치에서 데이터를 읽고 쓰기 위해서 자바에서 제공하는 클래스
  • 모든 스트림은 단방향
  • 하나의 스트림으로 입출력을 동시에 수행할 수 없으므로, 동시에 수행하려면 2개의 스트림 필요

스트림(Stream) 클래스 분류

▶ InputStream

  • 바이트 기반 입력 스트림의 최상위 클래스로 추상 클래스
  • 1byte 단위로 데이터를 입력하는 스트림
  • 1byte 범위 문자열 (아스키 코드 (영어, 숫자, 특수문자)) 또는 이미지, 영상, 오디오 등 문자가 아닌 파일 / 데이터

▶ FileInputStream

  • 파일을 바이트 단위로 읽을 때 사용
  • 그림, 오디오, 비디오, 텍스트 파일 등 모든 종류의 파일 읽기 가능
  • InputStream의 하위 클래스
public void byteInput() { // 바이트 기반 파일 입력

   // FileInputStream 객체 생성 시
   // FileNOtFoundException 예외가 발생할 가능성 있음 → 예외 처리 필요
   FileInputStream fis = null;

   try {
      fis = new FileInputStream("lyrics/APT.txt");
      
      // * 읽어올 땐 한 글자씩 *
      int value = 0; // 읽어온 한 글자를 저장할 변수 선언
      
      while(true) {
           value = fis.read(); // 다음 1byte를 읽어와 int로 저장 / 다음 내용이 없으면 -1 반환

           if(value == -1) break; // 다 읽었으면 while문 탈출
            
           System.out.print((char)value);
      }

   } catch(IOException e) { // IO 관련 코드는 IOException을 발생시킬 가능성이 높음
        e.printStackTrace(); // 예외가 발생한 메소드까지의 모든 내용 출력

   } finally { // 예외 발생여부 상관 없이 무조건 실행
           try {
                // 사용한 Stream 자원 반환(통로 없앰/메모리 반환)
                if(fis != null) fis.close(); 
           } catch (IOException e) {
                e.printStackTrace();
           }
   }
}

▶ OutputStream

  • 바이트 기반 출력 스트림의 최상위 클래스로 추상 클래스
  • 1byte 단위로 데이터를 출력하는 스트림
  • 1byte 범위 문자열 (아스키코드(영어, 숫자, 특수문자)) 또는 이미지, 영상, 오디오 등 문자가 아닌 파일 / 데이터

▶ FileOutputStream

  • 파일을 바이트 단위로 저장할 때 사용
  • 그림, 오디오, 비디오,  텍스트 파일 등 모든 종류의 데이터를 파일로 저장 
  • OutputStream의 하위 클래스
public void byteOutput() {

   // FileOutputStream 객체 생성 시
   // FileNotFoudException 예외가 발생할 가능성이 있음 -> 예외 처리 필요
   FileOutputStream fos = null;

   try {
      // byte 기반 파일 출력 Stream 생성
      fos = new FileOutputStream("byte/byteTest.txt");
      // byte 폴더 내부에 byteTest.txt 파일이 있으면 출력을 위해 연결하고,
      // 없으면 파일을 만들어서 연결

      String content = "Hello World\n"
                  + "IO 예제 작성중\n"
                  + 1234567
                  + "\n~!@#$%";

      for(int i=0; i<content.length(); i++) { // 글자 수만큼 반복
         // 한 문자씩 끊어서 파일로 출력하기
         fos.write(content.charAt(i)); // i번째 문자 하나
      }
	  System.out.println("출력 완료");
      
   } catch(IOException e) { // IO 관련 코드는 IOException을 발생시킬 가능성이 높음
         System.out.println("예외 발생");
         e.printStackTrace(); // 예외가 발생한 메소드까지의 모든 내용 출력

   } finally { // 예외 발생여부 상관 없이 무조건 실행
         // 사용한 Stream 자원 반환 (통로 없앰) --> 필수 작성!!
         // 프로그램 메모리 관리 차원에서 항상 다 쓰면 끊어줌
      try {
          if(fos != null) fos.close(); // 작성된 코드가 한 줄일 경우, 중괄호 생략 가능

      } catch (IOException e) {
          e.printStackTrace();
      }
   }
}

▶ Reader

  • 문자 기반 입력 스트림의 최상위 클래스로 추상클래스
  • 2byte 단위로 데이터(문자)를 입력하는 스트림
  • 2byte 범위의 문자열, 문자만 저장된 파일, 채팅, 인터넷 요청 시 데이터 전달 등에 이용

▶ FileReader

  • 문자 단위로 텍스트 기반 파일을 읽을 때 사용
  • 텍스트가 아닌 그림, 오디오, 비디오 등의 파일은 읽기 불가능
  • Reader의 하위 클래스
public void charInput() {
   FileReader fr = null;

   try {
      fr = new FileReader("lyrics/APT.txt");
      // * 읽어올 땐 한 글자씩 *
      int value = 0; // 읽어온 한 글자를 저장할 변수 선언

      while(true) {
           value = fr.read(); // 다음 1byte를 읽어와 int로 저장 / 다음 내용이 없으면 -1 반환

           if(value == -1) break; // 다 읽었으면 while문 종료

           System.out.print((char)value);
      }

   } catch(IOException e) {
          e.printStackTrace();

   } finally {
           try {
             if(fr != null) fr.close();
           } catch (IOException e) {
               e.printStackTrace();
           }
   }
}

▶ Writer

  • 문자 기반 출력 스트림의 최상위 클래스로 추상 클래스
  • 2byte 단위로 데이터(문자)를 출력하는 스트림
  • 2byte 범위의 문자열, 문자만 저장된 파일, 채팅, 인터넷 요청 시 데이터 전달 등에 이용

▶ FileWriter

  • 문자 단위로 텍스트 기반 파일 쓸(저장할) 때 사용
  • 텍스트가 아닌 그림, 오디오, 비디오 등의 파일은 저장 불가능
  • Writer의 하위 클래스
public void charOutput() {
   // 프로그램 -> 파일에 씀
   FileWriter fw = null;

   try {
    // fw = new FileWriter("경로", 이어쓰기 옵션_기본값:false); 
    // -> byte 기반 Stream도 사용 가능한 옵션
    fw = new FileWriter("char/charTest.txt", true);
    // char 폴더에 charTest.txt가 있으면 문자 출력 Stream 연결
    // 없으면 만들어서 연결

    String content = "\n오늘은 회식날 입니다.\n"
              + 1234567
              + "\n~!@#$%^\n"
              + "abcdefgh";

    fw.write(content); // 문자열을 통째로 내보냄
                       // -> 한 줄을 통째로 내보내기 위해 "버퍼"를 이용하는데
                      //     아직 버퍼에 담겨 있음!
                      // --> 이걸 강제로 밀어넣어서 버퍼를 비워야 함!
    System.out.println("char 기반 출력 완료");

   } catch(IOException e) {
         e.printStackTrace();

   } finally {
          try {
              // fw Stream 객체가 있을 때에만 닫기(메모리 반환)
              // close() 구문을 수행하면 통로에 남아있는 내용을 모두 내보내고
              // 통로를 없앤다!
              if(fw != null) fw.close();

          } catch (IOException e) {
                e.printStackTrace();
          }
   }
}

▶ 보조 스트림 종류

  • 문자 변환 : InputStreamReader / OutputStreamWriter
  • 입출력 성능 : BufferedInputStream / BufferedOutputStream
  • 기본 데이터 타입 출력 : DataInputStream / DataOutputStream
  • 객체 입출력 : ObjectInputStream / ObjectOutputStream

▶ 성능 향상 보조 스트림 (BufferedInputStream / BufferedOutputStream)

  • 느린 속도로 인해 입출력 성능에 영향을 미치는 입출력 소스를 이용하는 경우 사용
  • 입출력 소스와 직접 작업하지 않고, 버퍼에 데이터를 모아 한꺼번에 작업을 하여 실행 성능 향상(입출력 횟수 줄임)
public void fileCopy() {
   Scanner sc = new Scanner(System.in);

   // byte 기반 Stream 이용
   // + 성능 향상을 위한 보조 Stream 추가
   // -> BufferedInputStream / BufferedOuputStream
   // -> 모아서 한 번에 입/출력

   BufferedInputStream bis = null;  // 보조 Stream(Input 용)
   BufferedOutputStream bos = null; // 보조 Stream(Output 용)

   try {
      System.out.println("복사할 파일 경로 : ");
      String target = sc.nextLine(); // 한 줄 입력

      System.out.println("복사본이 저장될 경로 + 파일명 : ");
      String copy = sc.nextLine();

      // 복사 대상을 읽어올 InputStream 생성 + 보조 Stream으로 성능 향상
      bis = new BufferedInputStream( new FileInputStream(target) );
                // 보조 Stream( 기반 Stream ) : 보조 Stream 내부에 기반 Stream 넣기

      // 복사한 파일을 출력할 OutputStream 생성 + 보조 Stream으로 성능 향상
      bos = new BufferedOutputStream( new FileOutputStream(copy) );

      int value = 0; // 1byte씩 읽어서 저장할 임시 변수

      while(true) {
           value = bis.read(); // 1byte씩 읽어오기, 없으면 -1 반환

           if(value == -1) break; 

           bos.write(value); // 1byte씩 출력하기
      }
      System.out.println("복사 완료");

   } catch (FileNotFoundException e) { // 파일 경로 잘못 입력한 경우 예외 처리
      System.out.println("해당 파일이 존재하지 않습니다.");

   } catch(IOException e){ // IO 관련 예외 발생할 경우 예외 처리(IO예외 상위 클래스)
        e.printStackTrace();

   } finally {
        try {
            if(bis != null) bis.close(); // 보조 Stream을 close()하면
            if(bos != null) bos.close(); // 연결된 기반 Stream도 같이 close() 된다.
				
        } catch (IOException e) {
             e.printStackTrace();
        }
   }
}

▶ 객체 입출력 보조 스트림 (ObjectInputStream / ObjectOutputStream)

  • 객체를 파일 또는 네트워크로 입출력할 수 있는 기능 제공
  • 단, 객체는 문자가 아니므로 바이트 기반 스트림으로 데이터를 변경해주는 직렬화 필수

▶ 직렬화와 역직렬화

1) 직렬 (Serialization)

  • Serializable 인터페이스 implements 하여 구현
  • Serializable 인터페이스 : 해당 클래스가 객체로 만들어지면, 직렬화가 가능함을 나타냄을 표시 (마커 인터페이스)

2) 역직렬화 (Deserialization)

  • 직렬화된 객체를 역직렬화할 때는 직렬화했을 때와 같은 클래스 사용
  • 단, 클래스 이름이 같더라도 클래스 내용이 변경된 경우, 역직렬화 실패

3) serialVersionUID 필드

  • 직렬화한 클래스와 같은 클래스임을 알려주는 식별자 역할
  • 컴파일 시 JVM이 자동으로 추가
  • 단, 자동 생성 시 역직렬화에서 예상치 못한 InvalidClassException 유발할 수 있어 명시 권장
private static final long serialVersionUID = -123456789123456L;
public void objectOutput() { // 객체 출력 보조스트림
   // ObjectOutputStream : 객체를 바이트 기반으로 출력하는 보조 스트림
   ObjectOutputStream oos = null;

   try {
      // java.io.File : 파일/폴더를 참조하는 객체
      File folder = new File("object"); // object라는 이름의 파일/폴더를 참조

      if(!folder.exists()) { // object 폴더가 존재하지 않는다면 
             folder.mkdir(); // make directory(==folder) : 폴더 만들기
      }
			
      oos = new ObjectOutputStream( new FileOutputStream("object/Member.txt") );
      // 기반 스트림 : 생성된 스트림 객체를 이용해 직접 입출력을 수행할 수 있는 스트림
      // 보조 스트림 : 스트림 객체로 생성되어도 직접 입출력 할 수 없고,
      //              기반 스트림의 성능을 향상 시키거나 새로운 기능을 추가하기 위해 사용
			
      // 내보낼 회원 객체 생성
      Member mem = new Member("member01","pass01","일번멤버",1000);
			
      // 회원 객체를 파일로 출력
      oos.writeObject(mem);
			
      System.out.println("회원 출력 완료");
			
      // NotSerialization 발생 -> Member 클래스 Serializable 인터페이스 implements 필요
			
   } catch (IOException e) {
        e.printStackTrace();

   } finally {
           try {
             if(oos != null) oos.close();

           } catch (IOException e) {
                e.printStackTrace();
           }
   }
}
public void objectInput() { // 객체 입력 보조스트림
   ObjectInputStream ois = null;
		
   try {
      ois = new ObjectInputStream( new FileInputStream("object/Member.txt") );
			
      Member mem = (Member)ois.readObject();
      // readObject() : 직렬화된 객체 데이터를 읽어와서
      //                역직렬화시켜 정상적인 객체 형태로 반환
      // ois.readObject() : ClassNotFoundException, IOException 발생 가능 -> 예외처리
			
      System.out.println(mem);
			
   } catch (Exception e) { // 최상위 예외 클래스로 예외 한번에 처리
        e.printStackTrace();
			
   } finally {
           try {
             if(ois != null) ois.close();
				
           } catch (IOException e) {
                e.printStackTrace();
           }
   }
}

 

'Java > 기본 개념' 카테고리의 다른 글

변수(Variable)  (0) 2024.12.16
프로그래밍 기초  (0) 2024.12.16
컬렉션(Collection)  (2) 2024.11.17
스레드(Thread)  (0) 2024.11.17
네트워크(Network)  (0) 2024.11.17