개발일기/Java

File IO Stream과 Buffered IO Stream 속도 비교

민장미 2023. 5. 1. 23:46

IO Stream의 복사 원리 :

1. 1byte를 읽어 들인다.

2.  1byte를 적는다. 

3. 1,2를 read( )메소드가 -1을 반환할 때까지(파일을 다 읽음) 반복 (Buffered 도 같음)

 

파일 내 문자 다 읽어오기 루프문 사용 예시: 

while((readNum= bis.read()) != -1){

// Buffer단위로 작성

bos.write(readNum);

 

 

 

**번외 : Reader Stream일 경우엔 문자열을 한줄씩 읽어오고, 파일을 다 읽으면 null을 반환한다. **

((readV = br.readLine()) != null)

 

 

 

Buffered Stream를 이용해서 복사를 할 때, 

finally블록에서 자원을 해제할 때, 

close( ); 가 실행 전에  flush( ) 가 실행된다는 걸 기억하기 ***

flush()   => buffer를 비워주는 메소드 ***

다만, 출력 버퍼를 비우고 싶은 경우에는 flush() 메소드를 직접 호출해야 한다 ****

 

 

코드1 : FileInputStream, FileOutputStream을 이용하여 자바 설치프로그램을 복사해보기 (jdk2 

코드2: BufferedInputStream, BufferedOutputStream을 이용하여 자바 설치 프로그램을 복사해보기 (jdk3

 

 

결론을 말하자면 Buffered가 훠~~~얼씬 엄청 빠르다. 소요 시간이 6111 나왔다. 

(File IO는 기다리다가 포기할 뻔... 너무 느리다. 소요시간이 992663 나왔다. )

그렇다면 왜 버퍼가 훨씬 빠를까? 

 

앞의 포스팅에서 말했던 것처럼, IO Stream은 문자를 1byte씩 읽어오고 보낸다.

Buffer는 일종의 공간인데, 바구니나 카트역할을 한다고 생각하면 쉽게 이해가 가능하다.

1byte를 카트에 많이 담은 다음에 한번에 읽어들이거나 송출한다. 그래서 File IO Stream이 1차스트림이고,

Buffered  Stream이 2차 스트림이라고 불린다. 

 

1차스트림은 장치에 직접적으로 연결 되어있고, 2차 스트림은 1차 스트림에 연결이 되어 있다. 

그래서 2차 스트림(필터)의 객체를 생성할 때는, 1차 스트림도 생성 되어야한다. 

코드2의 아랫부분 참고!

bis = new BufferedInputStream(new FileInputStream(path1));

 

 

Buffered Stream의 객체를 생성할 때, 매개변수로 File IO Stream을 사용이 가능하다.  

(Buffered 생성자의 매개변수형은 IO Stream이다. !)

File Stream이나 Buffered Stream 둘 다  같은 최상위 추상 클래스,

InputStream, OutputStream을 조상으로 두고 있기 때문이다.   

다형성으로 Buffered, File 모두 IO Stream으로 묶을 수 있다! 

 

코드1:

package ex1;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Ex4_FileCopy {
public static void main(String[] args) {
	
	long start = System.currentTimeMillis();
	
	//반드시 원본파일은 존재해야한다. (input)
	String path1 = "C:\\\\KOSMO132\\\\java\\\\학원필기\\\\0501\\\\myFiles\\\\jdk.exe"; //원본 경로
	String path2 = "C:\\\\KOSMO132\\\\java\\\\학원필기\\\\0501\\\\myFiles\\\\jdk2.exe"; //복사할 경로
	//존재하는지 검사?
	File f = new File(path1);
	
	if(f.exists()) {
		System.out.println("해당 파일이 존재합니다.");
	}else {
		System.out.println("해당 파일이 존재하지 않습니다.");
	}
	//선언
	//1byte 씩 읽어들이면서 1byte씩 작성한다.
	FileInputStream fi = null; //읽어들인다. 
	FileOutputStream fis = null; 
	
	//생성 및 예외처리 ****
	
	try {
		fi = new FileInputStream(path1); //원본에 읽기 스트림 연결
		fis = new FileOutputStream(path2); //복사본에 쓰기 스트림 연결 
		// 1byte씩 읽어들일 변수
		// 이 변수에는 마지막(파일의 끝) -1값이 저장
		int readNum = 0;
		//파일의 끝까지 반복해서 읽어 들여야하기 때문에
		// fi.read() 1byte씩 읽어 들이면서 readNum에 저장
		// -1이되면 while문을 종료시킨다.
		while((readNum = fi.read()) != -1) {
			//System.out.println(readNum); 1바이ㅌ트씩 읽는다 매우 김
			//while문이 한번 동작될 때, 1 byte씩 읽은 값을 바로 복사본에 연결된
			//스트림을 통해서 작성한다.     
			//복사 원리 : 1byte읽고, 1byte 쓰기, 1byte 읽고, 1byte 쓰기.... 읽기가 -1이 나올때까지 (원본.read() = -1)
			fis.write(readNum);
			
		}
		
		
	} catch (FileNotFoundException e) {
		e.printStackTrace();
	}catch (IOException e) {
		e.printStackTrace();
	}
	finally {
		try {
			if(fi !=null) {
				fi.close();
			}
			if(fis !=null) {
				fis.close();
			}
		} catch (Exception e2) {
			e2.printStackTrace();
		}
	}
	
	long end = System.currentTimeMillis();
	System.out.println("복사 소요시간:"+(end- start));
	
	
}
}

 

코드 2: 

package ex1;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

// 바이트스트림 중에 장치에 직접 연결되는 주 스트림, 1차 스트림은
// 1바이트씩 읽어와서 전송하기 때문에
// 성능을 높이거나 특징을 제공해주는 보조스트림, 2차 스트림 필요
public class Ex5_BufferedCopy {
	public static void main(String[] args) {

		long start = System.currentTimeMillis();// 6111

		String path1 = "C:\\KOSMO132\\java\\학원필기\\0501\\myFiles\\jdk.exe"; // 원본 경로
		String path2 = "C:\\\\KOSMO132\\\\java\\\\학원필기\\\\0501\\\\myFiles\\\\jdk3.exe"; // 복사할 경로
		// 선언
		// 1byte씩 읽어들이면서 1byte씩 작성한다.!!!!
		//FileInputStream fis = null;
		//FileOutputStream fos = null;
		
		//버퍼 기능을 추가(계란판 완성)
		BufferedInputStream bis = null;
		BufferedOutputStream bos = null; //왜 밖에다가? finally에서 닫기 해주려고 
		

		try {
			//fis = new FileInputStream(path1); // 원본에 읽기 스트림 연결
			//fos = new FileOutputStream(path2); // 복사본에 쓰기 스트림 연결
			//버퍼 기능을 갖춘 2차 스트림 생성
			
			bis = new BufferedInputStream(new FileInputStream(path1)); //이렇게 하면 한번만 닫으면 된다. 
			bos = new BufferedOutputStream(new FileOutputStream(path2));
			//has a 관계 // bis가 바구니,카트 역할을 해준다. fis,fos 의 1byte씩 다담아서 한번에? 
			//얘도 장치라 닫아줘야한다. => 
			
			int readNum=0;
			//Buffer를 사용해서 버퍼 단위로 읽어오고 
			while((readNum= bis.read()) != -1){
				// Buffer단위로 작성 
				bos.write(readNum);
				
			}//wh
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				// bos일 경우 close()안에 flush()가 내장되어 있다.*****
				// flush(): 버퍼의 기능으로 장치에 작성할 때 버퍼를 비워주는 메서드 **
				// flush-> 필수는 아니지만 네트워트 환경시, 이게 없으면 전송이 안될 수도 있다. 
				//** 면접: flush로 버퍼를 비워주고 사용해야? 안 비워주면 데이터가 나가지 않는다. 
				//내부적으로 fis,fos가 함께 처리된다.   
				if (bis != null) {
					bis.close(); //close를 호출하기 전에 flush()가 호출된다고 생각하라 
				}
				if (bos != null) {
					bos.close();
				}
			} catch (Exception e2) {
				e2.printStackTrace();
			}
		}

		long end = System.currentTimeMillis();
		System.out.println("소요시간: " + (end - start));

	}
}