Java

파일 입력 스트림(바이트 기반)

H_u 2024. 5. 14. 11:36
728x90
반응형
SMALL

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

 

 

 

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.println((char)in.read());

// System.out.println((char)in.read());

// System.out.println((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) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

 

기반 스트림 (Base Stream)이란 뭘까?

기반 스트림은 데이터 소스와 직접 연결되는 스트림으로, 실제 데이터 읽기나 쓰기 작업을 수행합니다. 이 스트림들은 데이터의 실제 입력 소스(파일, 메모리, 네트워크 연결 등)에 직접 연결되어 데이터를 바이트 단위로 읽거나 쓰는 기본적인 기능을 제공합니다.

  • 바이트 기반 스트림: FileInputStream, FileOutputStream, SocketInputStream, SocketOutputStream 등이 있으며, 이들은 바이트 데이터를 처리합니다.
  • 문자 기반 스트림: FileReader, FileWriter 등이 있으며, 이들은 문자 데이터를 처리합니다.

보조 스트림 (Filter Stream)이란?

보조 스트림은 기반 스트림 또는 다른 보조 스트림에 연결되어 사용됩니다. 이 스트림들은 데이터를 변환하거나, 추가 기능을 제공하거나, 성능을 향상시키는 역할을 합니다. 보조 스트림은 자체적으로 데이터 소스에 연결되지 않으며, 항상 다른 스트림을 필요로 합니다.

  • 버퍼링: BufferedInputStream, BufferedOutputStream, BufferedReader, **BufferedWriter**는 내부적으로 버퍼를 사용하여 데이터를 모아서 한 번에 처리합니다. 이는 입출력 효율을 대폭 향상시킵니다.
  • 데이터 변환: DataInputStream, **DataOutputStream**은 기본 데이터 타입(int, float, long 등)을 쉽게 읽고 쓸 수 있게 해 줍니다.
  • 객체 직렬화: ObjectInputStream, **ObjectOutputStream**은 객체를 바이트 스트림으로 직렬화하거나 바이트 스트림에서 객체를 복원하는 기능을 제공합니다.
  • 문자 인코딩: InputStreamReader, **OutputStreamWriter**는 바이트 스트림을 문자 스트림으로 변환하거나 반대의 작업을 할 때 사용되며, 인코딩 변환을 처리합니다.

기반 스트림과 보조 스트림에 활용

FileInputStream fis = new FileInputStream("input.txt"); // 기반 스트림

BufferedInputStream bis = new BufferedInputStream(fis); // 보조 스트림

 

데코레이터 패턴

기반 스트림 + 보조스트림을 사용해 보자.

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.print((char)data);

}

} catch(Exception e) {

// 리소스 자원 정리 -> 반대로 -> 열었던 자원의 순서를 반대로 닫아주면 된다.

if(bfin != null)

try {

bfin.close();

} catch (IOException e1) {

e1.printStackTrace();

}

 

 

}

if(fin != null) {

try {

fin.close();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

 

}

 

 

작동 방식

  1. 첫 번째 read() 호출: **BufferedInputStream**은 내부 버퍼를 사용하여 데이터 소스로부터 많은 양의 데이터를 미리 읽습니다(예를 들어, 버퍼 크기가 8192바이트일 경우 최대 8192바이트를 읽음).
  2. 데이터 반환: 사용자가 **read()**를 호출할 때마다, **BufferedInputStream**은 버퍼에서 한 바이트씩 데이터를 반환합니다.
  3. 버퍼 재충전: 버퍼에 있는 모든 데이터가 읽혔다면, 다음 read() 호출 때 다시 데이터 소스에서 버퍼 크기만큼 데이터를 읽어 버퍼를 재충전합니다.

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

 

 

 

시나리오 코드1

package IO.file.ch02;

 

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.IOException;

 

public class MyFileOutputStream {

 

// 코드에 시작점

public static void main(String[] args) {

 

// 데이터가 존재

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();

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("파일이 없음");

} catch (IOException e) {

e.printStackTrace();

}

 

// 동작 이해해 보기

// 파일에 바이트 단위로 데이터를 쓰고

// 그 파일을 열었을 때 글자로 보이는 이유는 파일을 읽는 프로그램

// 파일 내에 바이트 데이터를 문자로 해석해서(문자 인코딩) 보여주었다.

 

} // end of main

 

}

 

시나리오 코드 2 - 보조 스트림

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(); // (더 정밀한 현재 시간을 출력)

String data = "기반 스트림 + 보조 스틈을 활용해보자";

 

// try catch resource 활용

try (FileOutputStream fos = new FileOutputStream("output2.txt");

BufferedOutputStream bos = new BufferedOutputStream(fos);) {

// 코드 수행 부분

byte[] bytes = data.getBytes();

 

// 보조 스트림 (버퍼)

bos.write(bytes);

// 간혹 버퍼에 데이터가 남아 있다면 중복된 데이터를 쓸 수 있다.

// 한번 사용한 다음에

bos.flush(); // flush --> 물을 내리다.

System.out.println("보조 스트림을 활용한 파일 출력 완료 ");

// 시간 측정을 해보고 싶다

 

} catch (Exception e) {

e.printStackTrace();

}

 

// 현재 시간 (종료)

long end = System.nanoTime();

// 종료 - 시작 -> 소요 시간

long duration = end - start;

System.out.println("소요 시간 : " + duration);

// 나노초는 10억 분에 1 -> 0.000287초

}

}


💡 중요 !!!!!!!

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

버퍼의 기본 원리

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

효율성 증가: 작은 데이터 조각들을 바로 전송하거나 저장하는 대신 큰 덩어리로 모아서 한 번에 처리합니다. 이 방식은 특히 입출력 연산이 자주 발생하는 상황에서 유용하며, 시스템의 입출력 호출 횟수를 줄여 전체적인 성능을 향상시킵니다. 시스템 부하 감소: 버퍼를 사용하면 데이터를 모아 두었다가 한 번에 처리하기 때문에 자원 사용을 더욱 효율적으로 관리할 수 있습니다. 이는 디스크 접근 횟수를 줄이거나 네트워크 트래픽을 최적화하는 데 도움을 줍니다. 데이터 전송 속도 개선: 데이터를 물리적 장치에 기록할 때, 장치의 처리 속도에 따라 기록 속도가 제한될 수 있습니다. 버퍼를 사용하면 데이터 전송 속도가 물리적 장치의 속도보다 빠르게 유지될 수 있으므로, 전체 데이터 전송 시간을 단축시킬 수 있습니다.

버퍼 사용의 단점

버퍼를 사용하는 것은 많은 이점이 있지만, 일부 단점도 있습니다. 예를 들어, 버퍼가 완전히 채워질 때까지 기다려야 하는 경우 실시간 처리에는 적합하지 않을 수 있습니다. 또한, 시스템이 예기치 않게 종료될 경우 버퍼에 저장된 데이터는 손실될 수 있습니다.
728x90
반응형
SMALL

'Java' 카테고리의 다른 글

표준 입출력이란?  (0) 2024.05.14
자바 I/O 개론  (0) 2024.05.14
나열된 수에서 최소값 최대값 및 위치 구하기  (0) 2024.05.13
Thread에 wait와 notify(프로듀서-컨슈머패턴)  (0) 2024.05.13
Map 인터페이스  (0) 2024.05.13