Programming Language/Java

입출력(IO) - 분류, 종류, 바이트스트림, 문자스트림, buffered 스트림, 객체 스트림

Ma_Sand 2022. 3. 14. 22:22
반응형

1. 입출력(IO)

  : Input & Output의 약자로, 컴퓨터 내외부 장치와 프로그램 사이에 데이터를 주고 받는 것을 뜻한다.

    입출력을 위해 스트림을 이용한다.

 

 

 

2. 스트림(Stream)

  : 데이터를 입출력 장치에서 읽고 쓰기 위해 Java에서 제공하는 클래스

    모든 스트림은 단방향이고, 하나의 스트림으로 입출력을 동시에 할 수 없어 2개의 스트림으로 동시에 수행해야 한다.

 

 (1) 분류

   ① 최상위 클래스

     ⓐ 바이트 스트림

       - 입력 스트림: InputStream

       - 출력 스트림: OutputStream

     ⓑ 문자 스트림

       - 입력 스트림: Reader

       - 출력 스트림: Writer

 

   ② 하위 클래스

     ⓐ 바이트 스트림

       - 입력 스트림: XXXInputStream

       - 출력 스트림: XXXOutputStream

     ⓑ 문자 스트림

       - 입력 스트림: XXXReader

       - 출력 스트림: XXXWriter

 

 

 (2) 종류

   ① Reader

     ⓐ 기반 스트림: FileReader

     ⓑ 보조 스트림: InputStreamReader, BufferedReader

 

   ② Writer

     ⓐ 기반 스트림: FileWriter

     ⓑ 보조 스트림: OutputStreamWriter, BufferedWriter

 

   ③ InputStream

     ⓐ 기반 스트림: FileInputStream

     ⓑ 보조 스트림: BufferedInputStream, ObjectInputStream, DataInputStream

 

   ④ OutputStream

     ⓐ 기반 스트림: FileOutputStream

     ⓑ 보조 스트림: BufferedOutputStream, ObjectOutputStream, DataOutputStream

 

     기반 스트림이 Input / Output 계열일 경우 보조 스트림도 같은 계열이고,

         기반 스트림이 Reader / Writer 계열일 경우 보조 스트림도 같은 계열이다. 

 

 

 (3) File 클래스

   ① 바이트 스트림

import java.io.FileInputStream;

public class FileByteDao{
    // 프로그램을 외부매체(파일)로 내보내기(출력) = 파일로 기록한다.
    public void fileOut() {
 
        // FileOutputStream : 1byte 단위로 파일을 출력한다.
    
        // FileOutputStream 객체를 생성한다.
        //  - 파일과 직접 연결하는 통로를 생성하며, 해당 파일이 존재할 땐 통로만 연결하고
        //    해당 파일이 존재하지 않을 땐 파일을 생성하면서 통로를 연결한다.
        FileOutputStream fos = null;
        // try문 안에 생성하면 finally 블록 내에서 사용이 불가하므로 밖에 적는다.(지역변수)
        
        try {
            fos = new FileOutputStream("ByteStream.txt", true);
            // 기존 파일이 있으면 false는 덮어쓰기를 하고, true는 이어쓰기를 한다.
            
            // 연결한 통로로 데이터를 전송한다. 이때, write() 메소드로 작성한다.
            // 음수를 제외한 1byte 짜리의 숫자를 출력할 수 있으며, 
            // 파일에는 해당 숫자의 고유 문자(아스키 코드)가 기록된다.
            fos.write(65);  // 대문자 A (아스키 코드)
            fos.write(66);  // 대문자 B
            
            // 배열
            byte[] arr = {65, 80, 80, 76, 69};  // APPLE(아스키 코드)
            fos.write(arr);  // APPLE
            fos.write(arr, 0, 2);  // APP, arr 배열의 0번 인덱스부터 2번 인덱스까지 출력
            
            // char타입 영어
            fos.write('A');  // A
            
            // char타입 한글
            fos.write('산');  // 한글은 2byte여서 깨져서 출력된다. 문자 스트림을 사용해야 한다.
            
            // 스트림을 다 쓴 후 반드시 자원을 반납한다.
            // 하지만 중간에 예외가 발생하면 자원 반납이 안된다.
            // fos.close();
        }
        catch(Exception e) {
            e.printStackTrace();  // 예외 발생 추적
        }
        finally {  // finally {}: 어떤 예외가 발생해도 반드시 실행하는 구문
            try {
                fos.close();  // 자원 반납(통로 닫기)
            }
            catch(IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    
    // 외부매체(파일)을 프로그램으로 가져오기(입력)
    public void fileIn() {
        
        // FileInputStream : 파일로부터 1byte 단위로 데이터를 가져온다.
        
        FileInputStream fis = null;
        // try문 안에 생성하면 finally 블록 내에서 사용이 불가하므로 밖에 적는다.(지역변수)
        
        try {
            // 반드시 존재하는 파일을 적어야 한다.
            fis = new FileInputStream("ByteStream.txt");
            
            // 1byte씩 데이터를 읽어온다. 이때, read() 메소드를 작성한다.
            // 방법1
            System.out.println(fis.read());
            System.out.println(fis.read());
            System.out.println(fis.read());
            System.out.println(fis.read());
            System.out.println(fis.read());
            System.out.println(fis.read());
            // 위 출력 메소드에서 6단어를 작성했으므로 가져올 문자도 6개이다.
            // 파일의 마지막을 읽어올 때 read() 메소드를 호출하면 -1을 반환한다.
            
            int value = 0;
            // fis.read() 메소드를 호출하는 순간 데이터를 읽어오기 때문에
            // value라는 변수에 데이터를 담아서 한번에 출력해야 한다.
            while((value = fis.read()) != -1) {
                System.out.println(value);
            }
            
            // 방법2     - 방법1이나 방법2 둘 중 하나의 방법으로 하면 된다.
            while(true) {
                int value = fis.read();
                if(value == -1) {
                    break;
                }
                System.out.println(value);
            }
        }
        catch(Exception e) {
            e.printStackTrace();
        }
        finally {
            try {
                fis.close();  // 자원 반납
            }
            catch(IOException e) {
                e.printStackTrace();
            }
        }
    }
}

 

 

    ② 문자 스트림

public class FileCharDao {
    
    // 출력
    public void fileSave() {
        
        // FileWriter : 2byte 단위로 데이터를 파일로 출력하는 스트림
        
        FileWriter fw = null;  // finally 블록에서 사용하기 위해 위에 뺴놓는다.(지역변수)
        
        try {
            // FileWriter 객체를 생성하여 파일과 연결된 통로를 만든다.
            fw = new FileWriter("CharStream.txt");
            
            // write() 메소드로 파일에 데이터 출력
            fw.write("맑은 날씨");  // 맑은 날씨
            fw.write('A');  // A
            
            // int 타입(1byte)을 입력하면 글자가 깨져서 나온다.
            fw.write(12);
            
            // 배열
            char[] ch = {'q', 'u', 'i', 'z'};
            fw.write(ch);  // quiz
        }
        catch(IOException e) {
            e.printStackTrace();  // 예외 발생 추적
        }
        finally {
            try {
                fw.close();  // 자원 반납
            }
            catch(IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    
    // 입력
    public void fileRead() {
        
        // FileReader : 파일로부터 2byte 단위로 데이터를 가져오는 스트림
        
        FileReader fr = null;
        
        try {
            // FileReader 객체를 생성하여 연결 통로를 만든다.
            fr = new FileReader("CharStream.txt");  // 기존에 있는 파일을 작성해야 한다.
            System.out.println(fr.read());
            
            int value = 0;
            while((value = fr.read()) != -1) {
                System.out.println((char)value);
                // char로 형변환 않고 value로만 데이터를 가져오면 아스키코드로 들어온다.
            }
        }
        catch(FileNotFoundException e) {
            e.printStackTrace();
        }
        catch(IOException e) {
            e.printStackTrace();
        }
        finally {
	        try {
		        fr.close();  // 자원 반납
			}  
            catch (IOException e) {
				e.printStackTrace();
			}
        }
    }
}

 

 

    ③ 새로 파일 생성하기

public class FileRun {
    public static void main(String[] args) {
        // File 클래스로 작업한다. ( java.io 패키지 )
        
        // File 클래스에서 객체를 생성한다.
        try {  // 전체를 감싸기
            File file1 = new File("OneFile.txt");
            try {
                file1.createNewFile();  // createNewFile() : 새로운 파일 생성
            }
            catch(IOException e) {
                e.printStackTrace();  // 예외 발생 추적
            }
            
            // File 객체 생성 경로를 지정하여 파일명을 적고 생성한다.
            File file2 = new File("C:\\Users\\FileClass\\TwoFile.txt");
            // C드라이브에 있는 Users 폴더 -> FileClass 폴더 -> 'TwoFile'라는 파일의 경로를 지정
            // 보통 경로 지정할 때 '/'를 사용하는데 여기에선 역슬래쉬(\)를 사용한다.
            try {
                file2.createNewFile();  // 새로운 파일 생성
            }
            catch(IOException e) {
                e.printStackTrace();
            }
            
            // 폴더를 생성한 후 그 안에 파일을 생성한다.
            File Folder = new File("C:\\Users\\FileClass\\TwoFile");
            Folder.mkdir();  // mkdir() : 새로운 디렉토리 생성
            
            File file3 = new File("C:\\Users\\FileClass\\TwoFile\\Hello World!");
            file3.createNewFile();
            
            // 별도의 경로를 지정하지 않고 폴더를 생성한 후 파일을 생성한다.
            File Folder2 = new File("newFolder");
            Folder2.mkdir();  // 새로운 디렉토리 생성
            
            File file4 = new File("newFolder\\text.txt");
            file4.createNewFile();  // 새로운 파일 생성
            
            
            // File 클래스에서 제공하는 메소드
            
            // isFile() : 파일이면 true, 폴더면 false를 반환한다.
            System.out.println("This is " + file1.isFile());  // true
            System.out.println("This is " + Folder.isFile());  // false
            
            // getName() : 파일명만 추출해서 반환한다.
            System.out.println("파일명: " + file1.getName());  // OneFile.txt
            // getParent() : 상위 폴더명만 추출해서 반환한다.
            System.out.println("상위 폴더: " + file1.getParent());  // null
            // length() : 파일의 크기를 반환한다.
            System.out.println("파일 용량: " + file1.length());  // 6
            // getAbsolutePath() : 시작점에서부터의 전체 경로를 반환한다.
            System.out.println("절대 경로: " + file1.getAbsolutePath());  
                                    // C:\Users\FileClass\OneFile.txt 
        }
    }
}

 

 

 

 (4) Buffered 보조 스트림

    : 버퍼 공간을 제공하여 데이터를 모아뒀다가 한번에 입출력한다.

      → 속도 향상을 목적으로 하는 보조 스트림이다.

          * 보조 스트림은 단독으로 존재할 수 없다.

            → 기반 스트림 객체를 생성한 후 보조 스트림 객체를 생성할 수 있다.

          * 표현: 보조스트림클래스명 객체명 = new 보조스트림클래스명(기반스트림객체);

 

   ① BufferedReader

     : 버퍼 공간을 제공하여 한번에 가져오는 스트림

 

   ② BufferedWriter

     : 버퍼 공간을 제공하여 한번에 출력하는 스트림

 

   ③ 예시

import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class BufferedDao {

	// 출력(프로그램을 외부매체(파일)로 보내기)
	public void fileSave() {
		
		FileWriter fw = null;  // finally 구문에서 사용해야 하므로 밖으로 빼놓는다.
		BufferedWriter bw = null;
		
		try {
			fw = new FileWriter("buffered.txt");  // 기반스트림 객체 생성
			bw = new BufferedWriter(fw);  // 보조스트림은 단독생성 불가
			
			bw.write("누구세요?");
			bw.write("홍길동입니다.");
			bw.newLine();  // newLine() : 개행 메소드
			bw.write("아, 그렇군요.");
			bw.flush();  // flush() : 버퍼에 남아있는 모든 문자열을 출력해준다.
			
		} 
        catch (IOException e) {
			e.printStackTrace();
		} 
        finally {
			try {
				bw.close();
				fw.close();   // 자원 반납시엔 생성된 자원 역순으로 반납
			} catch (IOException e) {
				e.printStackTrace();
			}  
		}
	}
	
	
    // 입력(외부매체(파일)로부터 프로그램으로 가져오기)
	public void fileRead() {
		FileReader fr = null;
		BufferedReader br = null;
		try {
			fr = new FileReader("buffered.txt");  // 존재하는 파일과 동일하게 작성
			br = new BufferedReader(fr);

		    System.out.println(br.read());
		    
		    // read로 1개씩 읽어오기
    	    int value = 0;
		    while((value = br.read()) != -1) {  // 마지막을 만나면 -1을 출력
		    	System.out.println((char)value);
		    }
		    
		    //readLine으로 한줄씩 읽어오기
		    String value = null;
		    while((value = br.readLine()) != null) {
		    	System.out.println(value);
		    }
		    
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}	catch(IOException e) {
			e.printStackTrace();
		} finally {
			try {
				br.close();  // 역순으로 자원 반납
				fr.close();
			}  catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	
	// try with resource 구문: 자동으로 자원 반납해주는 구문(jdk 1.7 이상부터 사용 가능)
	
	/*
	 * [표현법]
	 * try(스트림 객체 생성;) {
	 *      예외발생구문
	 * } catch(예외클래스명 변수) {
	 *     예외처리구문
	 * }
	 * 
	 * 스트림 객체를 생성하고 해당 블록이 실행 완료되면 자원 반납이 자동으로 이루어진다.
	 */

//	try(FileReader fr = new FileReader("buffered.txt"); 
//                BufferedReader br = new BufferedReader(fr)){
//		
//	} catch(Exception e) {
//		System.out.println("야호!");
//	}
	
	
	public void method3() {
		// 기반 스트림과 보조 스트림 생성 구문을 하나로 합치기
//		try(BufferedReader bis = new BufferedReader(new FileReader("buffered.txt"))){
//			String value = null;
//		    while((value = bis.read()) != -1) {
//		    	System.out.println(value);
//		    }
//		} catch(Exception e) {
//			e.printStackTrace();
//		}
		
		try(FileInputStream fis = new FileInputStream("C:\\Users\\newFolder\\img.png")){
			int value = 0;
			while((value = fis.read()) != -1) {
				System.out.println(value);
			}
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
}



// 실행할 클래스
import com.cd.assist.buffered.model.vo.BufferedDao;

public class BufferedRun {

	public static void main(String[] args) {
		BufferedDao bd = new BufferedDao();
		bd.fileSave();
		bd.fileRead();
        bd.method3();
	}

}

 

 

 

 (5) 객체 보조 스트림

   : 객체는 문자가 아니여서 바이트 기반 스트림으로 데이터를 변경해주는 직렬화(Serializable)가 필수이다.

   - 직렬화(Serializable)

     : implements Serializable 로 구현한다. private 필드를 포함하여 모든 필드를 바이트로 변환하는데,

       transient 키워드를 사용한 필드는 제외이다.

// Phone 객체
import java.io.Serializable;

// 객체를 스트림에 보내려면 직렬화를 해주어야 한다.(Serializable)
public class Phone implements Serializable {  
	private String name;
	private int price;
	
	public Phone() {
		
	}
	
	public Phone(String name, int price) {
		super();
		this.name = name;
		this.price = price;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getPrice() {
		return price;
	}

	public void setPrice(int price) {
		this.price = price;
	}
	
	@Override
	public String toString() {
		return "Phone [name=" + name + ", price=" + price + "]";
	}
}



// 객체 dao
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import com.cd.assist.object.model.vo.Phone;

public class ObjectDao {

	// 프로그램 -> 외부매체(파일)
	//출력
	public void fileSave(String fileName) {
		Phone p = new Phone("아이폰", 1520000);
		
//	    나눠서 쓰기(finally 구문을 작성하기 위해)
//		FileOutputStream fos = null;
//		ObjectOutputStream oos = null;
//		
//		fos = new FileOutputStream(fileName);
//		oos = new ObjectOutputStream(fos);
		
		// try with resource 구문에 나눠 쓰기
		try(FileOutputStream fos = new FileOutputStream(fileName); 
                    ObjectOutputStream oos = new ObjectOutputStream){
		}
		
		// 위에를 한번에 쓰기
		try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(fileName))){
			// 보조스트림에 기반스트림객체를 넣어 생성 완료
			
			oos.writeObject(p);  // writeObject로 Phone 객체 보내기
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}


    // 입력
	// 읽어오는 메소드 readObject 사용
	public void fileRead(String fileName) {
		try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName))) {

			System.out.println(ois.readObject());
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} 
	}
}



// 실행할 클래스
import com.cd.assist.object.model.dao.ObjectDao;

public class ObjectRun {
	public static void main(String[] args) {
		ObjectDao od = new ObjectDao();
		od.fileSave("Object.txt");  // 여기서 파일명 입력
		od.fileRead("Object.txt");
	}
}
반응형