본문 바로가기
Java

바이트 기반 - 파일 입출력/Copy

by 개발자공부 2024. 5. 20.

바이트 기반에 입력 스트림을 활용해서 파일에 있는 데이터 읽어보기

더보기
package io.file.ch01;

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

public class MyFileInputStream {

	public static void main(String[] args) {

		// 외부에 있는 파일 데이터를 읽을려면 input
		FileInputStream in = null;

		try {
			in = new FileInputStream("a.txt");

			// 1단게
//			int readData = in.read(); 

			// 2단계
//				System.out.print((char)in.read());
//				System.out.print((char)in.read());
//				System.out.print((char)in.read());

			// 3단계
			// 읽을 데이터가 더이상 없다면 -1을 반환한다. (약속되어 있는 것)
			int readData;
			while ((readData = in.read()) != -1) {
				System.out.print((char) readData);
			}

			// 4단계 FileInputStream(기반 스트림) 대상에 접근해서 한 바이트씩 읽어 들이는 기능을 가진다.
			// 1byte -> 127 -128 : a --> 97, A --> 65, 대 -->

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

}

 

기반 스트림 + 보조 스트림 활용

더보기
package io.file.ch01;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;

public class MyBufferedInputStream {

	public static void main(String[] args) {

		// 보조 스트림에 사용 --> 기반 스트림이 필요하다.
		FileInputStream fin = null;
		BufferedInputStream bfin = null;

		try {
			// 기반 스트림
			fin = new FileInputStream("a.txt");

			// 보조 스트림(기반 스트림)
			bfin = new BufferedInputStream(fin);

			int data;

			while ((data = bfin.read()) != -1) {
				System.out.println((char) data);

			}
		} catch (Exception e) {
			// 리소스 자원 정리 -> 반대로 -> 열었던 자원을 반대로 닫아 주면 된다.
			if(bfin != null) {
				try {
					bfin.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
			
			if(fin != null) {
				try {
					fin.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		}

	} // end of main

} // end of class
Hello World
a.txt

 

작동 방식

1.첫 번째 read() 호출 : BufferedInputStream은 내부 버퍼를 사용하여 데이터 소스로부터 많은 데이터를 미리 읽는다. 예를 들어 버퍼 크기가 8192바이트일 경우 최대 8192바이트를 읽는다.

2.데이터 반환 : 사용자가 read()를 호출할 때마다 BufferedInputStream은 버퍼에서 한 바이트씩 데이터를 반환한다.

3.버퍼 재충전 : 버퍼에 있는 모든 데이터가 읽혔다면 다음 read() 호출 때 다시 데이터 소스에서 버퍼 크기만큼 데이터를 읽어 버퍼를 재충전한다.

 

BufferedInputStream.read()를 호출하여 한 바이트씩 데이터를 읽는다. 이 때 실제 파일 접근은 버퍼가 비워질 때만 발생하므로 파일 접근 횟수가 줄어들어 성능이 향상된다.


 

바이트 기반에 출력 스트림을 활용해서 파일 생성 또는 파일 데이터를 출력해보기.

더보기
package io.file.ch02;

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

public class MyFile {

	long start = System.nanoTime();

	// 코드의 시작점
	public static void main(String[] args) throws IOException {
		// 데이터가 존재
		String data = "Hello, Java FileOutputStream \n안녕 반가워";
		// 1. 파일에다가 데이터를 저장하고 싶다면(바이트 기반)
		// 자바 8버전부터 제공
		// try-catch-resource -> 자동으로 자원 닫아준다

		try (FileOutputStream fos = new FileOutputStream("output.txt", true)) {
			// 수행코드
			// data(String) 가지고 있는 문자열들을 바이트 배열로 반환 처리!!
			// byte[] bytes = data.getBytes(); // 스트링을 바이트로 출력
			byte[] bytes = { 72, 101, 108, 108, 111 }; // Hello 문자열

			System.out.println("bytes : " + bytes);
			for (int i = 0; i < bytes.length; i++) {
				System.out.println("bytes[i] : " + bytes[i]);
			}

			// 연결된 스트림을 활용해서 바이트 배열을 파일에다가 흘려보냄
			fos.write(bytes);

			System.out.println("파일출력스트림을 통해서 데이터를 씀");

		} catch (FileNotFoundException e) {
			System.out.println("파일이 없음");
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		// 동작 이해해보기
		// 파일에 바이트 단위로 데이터를 쓰고
		// 그 파일을 열었을 때 글자로 보이는 이유는 파일을 읽는 프로그램이
		// 파일 내에 바이트 데이터를 문자로 해석(문자 인코딩)해서 보여주었다.

		long end = System.nanoTime();
//		long duration = end - start;
//		System.out.println("기반 스트림 파일 입출력 소요 시간 : " + duration);

	} // end of main
}

 

보조 스트림 사용

더보기
package io.file.ch02;

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;

public class MyBufferedOutputStream {

	public static void main(String[] args) {
		
		// 현재 시간(시작)
		long start = System.nanoTime(); // --> 더 정밀한 현재 시간을 출력
//		System.currentTimeMillis();
		String data = "기반 스트림 + 보조 스트림을 활용해보자";

		// try - catch - resource 활용
		try (FileOutputStream fos = new FileOutputStream("output2.txt");
				BufferedOutputStream bos = new BufferedOutputStream(fos);) {
			// 보조 생성(bos)될 때 기반(fos)을 넣어준다..
			// 코드 수행 부분
			byte[] bytes = data.getBytes();
			// 보조 스트림(버퍼)
			bos.write(bytes);
			// 간혹 버퍼에 데이터가 남아있다면 중복된 데이터를 쓸 수 있다.
			// 한 번 사용한 다음에
			bos.flush(); // flush --> 물을 내리다. // spring boot를 위해 꼭 이해해야 하는 개념!

			System.out.println("보조 스트림을 활용한 파일 출력 완료");
			// 시간 측정을 해보고 싶다.
		} catch (Exception e) {
			e.printStackTrace();
		}

		// 현재 시간(종료)
		long end = System.nanoTime();
		// 종료 -(빼기) 시작 -> 소요 시간
		long duration = end - start;
		System.out.println("소요시간 : " + duration);
		// 나노초는 10억 분에 1 -> 0.000287초
	} // end of main

} // end of class

 

버퍼는 입출력 작업에서 매우 중요한 역할은 한다. 원리를 이해하면 데이터 처리 효율성을 높이는데 큰 도움이 된다. 사용 원리는 기본적으로 '일괄 처리' 또는 '집단 처리' 방식에 비유할 수 있다.

 

버퍼 기본 원리

버퍼는 임시 저장 공간을 말하며, 데이터를 최종 목적지(파일, 네트워크, 디스플레이 등)에 쓰기 전에 일시적으로 데이터를 모아 두는 역할을 한다.

 

효율성 증가 : 작은 데이터 조각들을 바로 전송하거나 저장하는 대신 큰 덩어리로 모아서 한 번에 처리한다. 이 방식은 특히 입출력 연산이 자주 발생하는 상황에서 유용하며, 시스템 입출력 호출 횟수를 줄여 전체적인 성능을 향상시킨다.

시스템 부하 감소 : 버퍼를 사용하면 데이터를 모아 두었다가 한 번에 처리하기 때문에 자원 사용을 더욱 효율적으로 관리할 수 있다. 이는 디스크 접근 횟수를 줄이거나 네트워크 트래픽을 최적화하는데 도움을 준다.

데이터 전송 속도 개선 : 데이터를 물리적 장치에 기록할 때 장치의 처리 속도에 따라 기록 속도가 제한될 수 있다. 버퍼를 사용하면 데이터 전송 속도가 물리적 장치의 속도보다 빠르게 유지될 수 있으므로 전체 데이터 전송 시간을 단축시킬 수 있다.

 

버퍼 사용 단점

예를 들어 버퍼가 완전히 채워질 때까지 기다려야 하는 경우 실시간 처리에는 적합하지 않을 수 있다. 또한 시스템이 예기치 않게 종료될 경우 버퍼에 저장된 데이터는 손실될 수 있다.

 


 

기반 스트림인 파일 입력, 출력 스트림을 사용해서 카피 파일 만들기

더보기
package io.file.ch03;

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

public class FileCopy {

	public static void main(String[] args) {

		// 스트림은 기본적으로 단방향이다.
		// 입력 스트림, 출력 스트림 두 개가 필요하다.

		// 파일 경로 (복사할 대상)
		String sourceFilePath = "D:\\workspace_ohj\\java\\tenco_swing\\img\\a.zip";
		// 목적지 파일
		String destinationFilePath = "copy1.zip";

		// 소요시간 확인
		long startTime = System.nanoTime();

		try (FileInputStream in = new FileInputStream(sourceFilePath);
				FileOutputStream out = new FileOutputStream(destinationFilePath)){
			int data;
			while( (data = in.read() ) != -1  ) {
				// 파일에 출력
				out.write(data);
			}
			System.out.println("입력스트림--> 출력스트림 --> 입력-->출력 에 반복 완료");
		} catch (Exception e) {
			// TODO: handle exception
		}

		long endTime = System.nanoTime();
		long duration = endTime - startTime;
		System.out.println("복사하는데 소요 시간 : " + duration);

		// 소요 시간을 추 단위로 변환 --> 포맷팅
		double seconds = duration / 1_000_000_000.0; // 더블 타입이 연산이 정확해서 소수점 추가

		// String 클래스에 format 메서드 사용해보기
		String resultFormat = String.format("소요시간은 : %.6f초 입니다.", seconds);
		// % 는 포맷 지정자의 시작
		// f 지정자는 float, double 유형의 변수를 인자로 받아 처리하겠다.
		System.out.println(resultFormat);
	} // end of main

}

 

'Java' 카테고리의 다른 글

Map 인터페이스  (0) 2024.05.24
문자 기반 스트림 - 파일 입출력/Copy/ZIP  (1) 2024.05.21
I/O, 표준 입출력  (0) 2024.05.20
Thread, multi-threading (스레드)  (0) 2024.05.01
Exception(예외처리)  (0) 2024.04.29