다잘하고싶어

자바 파일 입출력 본문

이론학습/JAVA

자바 파일 입출력

챙영잉 2023. 2. 21. 10:31

목차

☑️ I/O와 Stream

I/O

  • 데이터의 입력(input)과 출력(output)
  • 데이터는 한쪽에서 주고 한쪽으로 받는 구조 ( 일방통행 )

스트림

  • 바이트가 흘러가는 통로

I/O 처리 단위

 

  byte ( 바이트 스트림)  Char(문자 스트림)
입력 InputStream Reader
출력 OutputStream Writer

⇒ 4가지 모두 추상클래스

⇒ 실제 사용하는 입출력 객체들은 위의 4가지의 하위 클래스 이다.

☑️ 바이트 스트림

⇒ 바이트가 흘러가는 통로

☑️ 문자 스트림

⇒ 문자가 흘러가는 통로

☑️ InputStream 의 주요메서드 (입력스트림 _ 바이트)

✅ read()

public abstract int read() throws IOException

⇒ 매개변수 없음

⇒ byte 하나를 읽어서 int 로 반환. 더이상 읽을 값이 없으면 -1 반환.

public int read(byte b[]) throws IOException

⇒ 매개변수 바이트배열 ( 하나씩 읽어오면 비효율적, 한방에 여러개씩 보내기)

⇒ 데이터를 읽어서 b를 채우고, 읽은 바이트의 개수를 리턴.

⇒ 0 이 되면 더이상 읽을 값이 없는 상황.

 

public int read(byte b[], int offset, int len) throws IOException

⇒ 최대 len 만큼 데이터를 읽어서, b의 offset 부터 b에 저장하고 읽은 바이트 개수를 리턴.

⇒ len + offset 은 b의 크기 이하여야 함.

☑️ Reader 의 주요메서드 (입력스트림 _ 문자)

public int read() throws IOException

⇒ char 하나를 읽어서 int 로 반환. 더이상 읽을 값 없으면 -1 리턴

public int read(char cbuf[]) throws IOException

⇒ 데이터를 읽어서 cbuf를 채우고, 읽은 문자의 개수를 리턴.

☑️OutputStream 의 주요메서드(출력스트림 _ 바이트)

write()

⇒ 바이트를 다루는 메서드지만, 묵시적 형변환에 의해 int 로 다룰 수 있다.

☑️ Writer의 주요메서드(출력스트림 _ 문자)

✅ OutputStream 과 Writer의 flush ()

⇒ 버퍼가 있는 스트림에서 버퍼의 내용을 출력하고 버퍼를 비운다

✉️ File

⇒ 가장 기본적인 입출력 장치 중 하나로 파일과 디렉터리를 다루는 클래스

⇒ “input.txt” ⇒ new File(”input.txt”)

☑️ FileInputStream, FileOutputStream, FileReader, FileWriter

❗입출력에는 항상 자원이 소모되므로 사용 후 닫아주는 과정 필수❗

실습코드(1) 파일 복사

  • try finally 문을 사용한다
  • 왜? finally 는 언제나 실행되므로 사용중인 자원을 반납할 때( 입출력 스트림 사용 시) 사용
  • 따라서 아래 코드는 예외처리의 과정이 아님.

 

빨간줄 → throws 로 처리

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

public class Test1 {
	public static void main(String[] args) throws IOException {
		FileInputStream in = null;
		FileOutputStream out = null;

		try {
			in = new FileInputStream("newjeans.jpg"); // 파일이름
			out = new FileOutputStream("newjeans-copy.jpg");// 복사하기

			int b;// byte 를 int형으로 저장해도 됨
			while ((b = in.read()) != 1) {
				out.write(b);
			}
			System.out.println("복사를 완료했습니다.");

		} finally {
			if (in != null) {
				in.close();
			}
			if (out != null) {
				out.close();
			}
			System.out.println("모든 리소스를 닫고 종료합니다.");
		}
	}

}

⇒ 복사된 파일 생성된 것 확인가능

실습코드(2) try with resources사용

try with resources

  • try 다음에 ( ) ( ) … 가 옴
  • ( ) 안에 필요한 리소스를 정의
  • 구분은 ; 로 함
  • close 할 필요 없이 알아서 close 해줌
public class Test3 {
	public static void main(String[] args) throws IOException { 
		
		try (FileInputStream in = new FileInputStream("newjeans.jpg");
			 FileOutputStream out = new FileOutputStream("newjeans-copy22.jpg");){ 
			int b;//byte 를 int형으로 저장해도 됨/
			while( (b = in.read()) != 1) {
				out.write(b);
			}
			System.out.println("복사를 완료했습니다.");
			System.out.println("알아서 스트림을 닫아줍니다. 종료합니다."); 
			
		} 
	}
}

실습코드(3) buffer 사용

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;

public class Test2 {
	public static void main(String[] args) throws IOException { 
		
		try (FileInputStream in = new FileInputStream("newjeans.jpg");
			 FileOutputStream out = new FileOutputStream("newjeans-copy33.jpg");
				){ 
			
			//버퍼를 사용하기
			byte[] buffer = new byte[10];
			int read; //byte 를 int 형으로 저장해도 됨. 
			while( (read = in.read(buffer)) != -1) { 
				out.write(buffer, 0, read); 
				System.out.println(Arrays.toString(buffer) + " , "+ read);
			}
			System.out.println("복사를 완료했습니다.");
			System.out.println("알아서 스트림을 닫아줍니다. 종료합니다."); 
			
		} 
	}
	

}

⇒ 매번 .read(buffer) 할 때마다 알아서 읽어오면서 buffer 에 바이트를 채워줌.

  ( buffer 는 배열이름)

⇒ read : 어디까지 읽으면 되는지 저장, 더이상 읽을 게 없다면 -1 반환

⇒ out.write(buffer, 0, read);//버퍼를 어디서부터 어디까지 사용하는지?

Q. 위의 코드에서 왜 마지막 3개의 값이 같을까? (feat. write() 메서드 이해하기)

코드에서 길이를 표현하는 변수를 read 로 선언하고, 버퍼배열 출력 후 출력되도록 했다.

맨 마지막에 출력된 read (길이) 의 값은 7개임 ( while문에서 더이상 읽을 게 없어 -1 반환된 것)

즉, 마지막의 3개의 값은 새롭게 채워지지 않았으므로(그 전에 다 읽어서 while문 멈춤), 그 직전의 미리 채워져있던 버퍼 배열 안의 값이 그대로 유지된 것이다.

실습코드 (4) _ 문자스트림

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

public class Test1 {
	public static void main(String[] args) throws IOException  {
		try(FileReader reader = new FileReader("big_input.txt");
				FileWriter writer = new FileWriter("big_input_copy.txt")){
			int c ; //character 를 int 에 담아도(묵시적)
			while((c=reader.read())!= -1) {
				writer.write(c); 
			}
			System.out.println("복사를 완료했습니다");
			System.out.println("try with resource 구문 사용 - 알아서 정리");
		} 	
	} 
}

☑️ 보조스트림

✅ 보조스트림 : Filter Stream, Processing Stream

  • 다른 스트림에 부가적인 기능을 제공하는 스트림
  • 문자를 set 으로 변환
  • Buffering 기능 추가
  • 기본 데이터 형으로 변형 ( byte → 정수, 실수로 변환)

보조 스트림의 종류

 

✅ 생성 (보조스트림 사용법)

→ 이전 스트림을 생성자의 파라미터에 연결

✅ 종료 :

→ 보조 스트림의 close() 를 호출하면 노트스트림의 close() 까지 호출

✅ 사용할 스트림 결정 과정

1️⃣ 노드가 무엇인지 확인 2️⃣ 타입이 문자열인지 바이트인지 확인 (노트 스트림 구성) 3️⃣ 방향이 무엇인가 4️⃣ 추가 기능이 필요한가( 보조스트림 구성)

 

예시 1

예시 2

예시 3

☑️ Buffered 계열 → 버퍼기능이 추가된것

과연 시간이 더 빠를까?

✅ [ FileReader & FileWriter ] VS [ BufferedReader& BufferedWriter ]

예외 처리 안해서 빨간줄 뜸. throws 로 넘기기

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class Test1 {
	public static void main(String[] args) throws IOException {
		//FileReader, FileWriter
		test1("    FileReader & FileWriter    "); 
		//BufferedReader, BufferedWriter 
		test1("BufferedReader & BufferedWriter");
	}
	
	//FileReader, FileWriter 사용
	public static void test1(String testname) throws IOException {
		try(FileReader reader = new FileReader("big_input.txt");
				FileWriter writer = new FileWriter("big_input_copy3.txt")){
			long start = System.nanoTime();
			int c;
			while((c = reader.read()) != -1) {
				writer.write(c);
			}
			long end = System.nanoTime();
			System.out.printf("%s - %15d ns. \\n", testname, end-start);
		}
	}
	
	//보조스트림 BufferedReader,BufferedWriter 사용
	public static void test2(String testname) throws IOException {
		try(BufferedReader reader = new BufferedReader(new FileReader("big_input.txt"));
			   BufferedWriter writer = new BufferedWriter(new FileWriter("big_input_copy2.txt"))){
			long start = System.nanoTime();
			
			String line;
			while((line = reader.readLine()) != null) {
				writer.write(line);
				writer.newLine();
			}
			long end = System.nanoTime();
			System.out.printf("%s - %15d ns. \\n", testname, end-start);
					
		}
	}

}

BufferedReader, BufferedWriter 는 character 하나씩 읽어오지않고 line 단위로 읽어온다.

 

결과

⇒ 보통은 10배정도 차이가 난다.

Scanner vs BufferedReader

package test04;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Scanner;

public class Test1 {
	public static void main(String[] args) throws IOException {
		//Scanner vs BufferedReader
		//big_input.txt 에서 한 줄 씩 읽어와서 정수형으로 바꾸기
		test1("    Scanner    : ");
		test2("BufferedReader : ");
	}
	
	//Scanner
	public static void test1(String testname) throws IOException {
		try(Scanner scan = new Scanner(new FileInputStream("big_input.txt"))){
			long start = System.nanoTime();
			
			while(scan.hasNext()) {
				int num = scan.nextInt();
			}

			long end = System.nanoTime();
			System.out.printf("%s - %15d ns. \\n", testname, end-start);
					
		}
	}
	
	// BufferedReader
	public static void test2(String testname) throws IOException {
		try(BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("big_input.txt")))){
			long start = System.nanoTime();
			
			String l;
			while((l=br.readLine())!=null) {
				int num = Integer.parseInt(l);
			}
			
			long end = System.nanoTime();
			System.out.printf("%s - %15d ns. \\n", testname, end-start);
					
		}
	}

}

결과

⇒ FileInputStream 은 character, 이걸 byte 로 읽어야 한다 → 필요한 것? InputStreamReader 라는 보조스트림 필요.

⇒ 즉, 파일을 FileInputStream 으로 받아와서, BufferedReader로 읽기 위해서 InputStreamReader 스트림을 통해 가져온다.

 

inputstream 을 fileReader 로 가져올 거임

표준입출력 → 알고리즘

System.in ← InputStream
System.out ← OutputStream

ex. 키보드에서 입력받을 때
BufferedReader in = new BufferedReader( new InputStreamReader(System.in));

파일클래스의 메소드

public class Test1 {
	public static void main(String[] args) {
		//File 클래스
		//file 또는 directory의 객체 생성
		File f = new File("big_input.txt");
		System.out.println("이름 : "+ f.getName());
		System.out.println("경로 : "+ f.getPath());
		System.out.println("디렉토리 여부 : "+ f.isDirectory());
		System.out.println("파일 여부 : "+ f.isFile());
		System.out.println(f.toString());
		
	}
}

'이론학습 > JAVA' 카테고리의 다른 글

자바 객체직렬화  (1) 2023.02.21
자바 예외처리  (0) 2023.02.16
자바 컬렉션  (0) 2023.02.13
자바 동적바인딩 , 추상클래스 보충설명, 인터페이스 보충설명  (0) 2023.02.13
자바 인터페이스  (0) 2023.02.06