본문 바로가기
Java/Java

자바입출력/byte기반) 파일을 대상으로한 입출력 (FileInputStream, FileOutputStream)

by 박채니 2022. 3. 29.

안녕하세요, 코린이의 코딩 학습기 채니 입니다.

 

개인 포스팅용으로 내용에 오류 및 잘못된 정보가 있을 수 있습니다.


자바 입출력(java IO)

- 프로그램을 기준으로 외부로부터 데이터가 들어오는 입력(input)과 프로그램에서 외부로 나가는 출력(output)으로 구성

- 입력의 대상 : 키보드/마우스/파일/네트워크 등

- 출력의 대상 : 화면/프린터/파일/네트워크/콘솔 등

 

자바 입출력 종류

① byte 단위 입출력

  - 송수신하고자 하는 데이터를 byte 단위로 쪼개 보내고 받는 것

② char 단위 입출력

  - 텍스트 전송에 특화된 방법

 

 

입력의 최상위 추상 클래스인 InputStream을 상속해 추상 메소드를 구현한 대표적인 자식 클래스에는 FileInputStream, BufferedInputStream, DataInputStream 등이 있습니다.

자바에서 콘솔 입력을 위해 미리 객체를 생성해 제공하는 System.in도 InputStream을 구현한 클래스의 객체 입니다.

 

출력의 최상위 추상 클래스인 OutputStream을 상속해 추상 메소드를 구현한 대표적인 자식 클래스에는 FileOutputStream, BufferedOutputStream, DataOutputStream 등이 있습니다.

자바에서 콘솔 출력을 위해 미리 객체를 생성해 제공하는 System.out도 PrintStream 타입의 객체 입니다.

 

InputStream 주요 메소드

int available() inputStream의 남은 바이트 수를 리턴
abstract int read() int(4byte)의 하위 1byte에 읽은 데이터를 저장해 리턴 (추상메소드)
void read(byte[] b) 읽은 데이터를 byte[] b의 0번째 위치부터 저장하며 읽은 바이트 수를 리턴
int read(byte[] b, int off, int len) len 개수만큼 읽은 데이터를 byte[] b의 off 위치부터 저장
void close() InputStream의 자원 반환

 

OutputStream 주요 메소드

void flush() 메모리 버퍼에 저장된 output stream 내보내기(실제 출력 수행)
abstract void write (int b) int(4byte)의 하위 1byte를 output 버퍼에 출력(추상 메소드)
void write(byte[] b) 매개변수로 넘겨진 byte[] b의 0번째 위치에서부터 메모리 버퍼에 출력
void write(byte[] b, int off, int len) byte[]의 off 위치에서 len개를 읽은 후 출력
void close() OutputStream의 자원 반환

 


파일을 대상으로한 입출력

 

☞ FileInputStream

- 대상이 외부파일인 경우 사용하는 바이트 기반 입력 스트림

- 파일이 존재하지 않은 경우, FileNotFoundException 발생 (checked Exception)

- 사용 후에는 반드시 반납

//객체 생성 방법 1) (String)
FileInputStream fis1 = new FileInputStream("helloworld.txt");
		
//객체 생성 방법 2) (File)
File file = new FIle("helloworld.txt");
FileInputStream fis2 = new FileInputStream(file);

 

FileInputStream의 객체를 생성하는 방법으로 2가지가 있습니다.

FileInputStream(String name) 매개변수로 넘어온 name 위치의 파일을 읽기 위한 InputStream 생성
FileInputStream(File file) 매개변수로 넘어온 file을 읽기 위한 InputStream 생성

 

파일 읽어오기

 

파일 생성

//helloworld.txt
1234567890
abcdefg
가나다라마바

files폴더 하위에 helloworld.txt 파일을 생성하여 파일을 읽어와보았습니다.

 

FileInputStream fis = null;
		
try {
	fis = new FileInputStream("files/helloworld.txt");
	
	int data = 0;	//byte기반은 int를 리턴
    
	//파일을 모두 읽으면 -1을 리턴
	while((data = fis.read()) != -1) {
		System.out.print((char)data + " ");
	}
} catch(FileNotFoundException e) {
	e.printStackTrace();
} catch(IOException e) {
	e.printStackTrace();
} finally {
	try {
		fis.close();
	} catch(IOException e) {
		e.printStackTrace();
	}
}

@콘솔출력값
1 2 3 4 5 6 7 8 9 0 
 
 a b c d e f g 
 
 ê ° € ë ‚ ˜ ë ‹ ¤ ë  ¼ ë § ˆ ë ° ”

마찬가지로 한글은 3byte 처리 된 것을 1byte씩 쪼개어 읽어오다보니 글자가 깨지는 현상이 발생하였습니다.

 

read()메소드를 통해 읽어온 값을 반환하여 data에 담아 출력하였습니다.

 

또한, 모든 작업이 끝나면 사용했단 FileInputStream 자원을 close() 메소드를 이용해 반납하였습니다.

이렇게 되면 FileInputStream 객체는 더 이상 사용할 수 없으며, 파일과의 연결도 종료됩니다.

반드시 사용 후 반납해야 하기 때문에 finally 절에서 로직을 짰습니다.

 

 

☞ FileOutputStream

- 대상이 외부파일인 바이트 기반의 출력 스트림

- 파일이 존재하지 않으면, 새로 생성

- 사용 후에는 반드시 반납

//객체 생성 방법 1) (String)
FileOutputStream fos1 = new FileOutputStream("files/copy.txt");
		
//객체 생성 방법 2) (String, append)
FileOutputStream fos2 = new FileOutputStream("files/copy.txt", true);
		
//객체 생성 방법 3) (File)
File file1 = new File("files/copy.txt");
FileOutputStream fos3 = new FileOutputStream(file1);
		
//객체 생성 방법 4) (File, append)
File file2 = new File("files/copy.txt");
FileOutputStream fos4 = new FileOutputStream(file2, true);

 

FileOutputStream의 객체를 생성하는 방법으로 4가지가 있습니다.

FileOutputStream(String name) 매개변수로 넘어온 name 위치의 파일을 쓰기 위한 OutputStream 생성,
append = true일 때 이어쓰기
append = false일 때 덮어쓰기 (default = false)
FileOutputStream(String name, boolean append)
FileOutputStream(File file) 매개변수로 넘어온 file을 쓰기 위한 OutputStream 생성
append = true일 때 이어쓰기
append = false일 때 새로 넘어쓰기(default = false)
FileOutputStream(File file, boolean append)

 

파일 출력하기

FileInputStream fis = null;
FileOutputStream fos = null;
		
try {
	fis = new FileInputStream("files/helloworld.txt");
	fos = new FileOutputStream("files/copy.txt");

	int data = 0;
	//파일을 모두 읽으면 -1을 리턴
	while((data = fis.read()) != -1) {
		System.out.print((char)data + " ");	//콘솔출력
		fos.write(data);
	}
} catch(FileNotFoundException e) {
	e.printStackTrace();
} catch(IOException e) {
	e.printStackTrace();
} finally {
	try {
		fis.close();
	} catch(IOException e) {
		e.printStackTrace();
	}
			
	try {
		fos.close();
	} catch(IOException e) {
		e.printStackTrace();
	}
}

마찬가지로 사용한 FileOutputStream 자원을 close()메소드를 이용해 반납하여야 하는데,

fis.close() 가 있는 try절에 한 번에 묶어서 예외처리를 하면 좋겠지만 그렇게 되면 fis.close() 수행 시 IOException 이 발생하게 된다면 하위 구문은 실행하지 않은 채로 catch(){} 블록으로 넘어가기 때문에 FileOutputStream 자원에 대한 반납이 이루어지지 않게 됩니다.

따라서 다소 번거롭지만 try-catch문을 하나 더 사용하여 자원 반납을 해주었습니다.

 

만일 파일이 존재하는 경우 덮어쓰기/이어쓰기가 되고, 파일이 존재하지 않은 경우에는 파일을 새로 생성합니다.

"files/copy.txt" 파일은 존재하지 않기 때문에 "files/copy.txt" 파일이 새로 생성되었고, 기존 "files/helloworld.txt"의 내용을 읽어와 "files/copy.txt"파일에 썼기 때문에 동일한 내용의 파일이 생성되었습니다.

파일 생성

//files/copy.txt
1234567890
abcdefg
가나다라마바

 

컴퓨터 내부의 이미지 파일을 files/파일명.jpg로 복사

FileInputStream fis = null;
FileOutputStream fos = null;
		

try {
	fis = new FileInputStream("C:\\Workspaces\\java_workspace\\photo.jpg");
	fos = new FileOutputStream("files/copyPhoto.jpg");

	int data = 0;
	while((data = fis.read()) != -1) {
		System.out.println(data);
		fos.write(data);
	}
} catch(IOException e) {
	e.printStackTrace();
} finally {
	try {
		fis.close();
	} catch(IOException e) {
		e.printStackTrace();
	}
	try {
		fos.close();
	} catch(IOException e) {
		e.printStackTrace();
	}
}

@콘솔출력값
174
154
199
141
13
232
221
55
83
83
120
...
...

파일 생성

copyPhoto.jpg

컴퓨터에 저장 되어 있는 이미지 파일을 가져와 써보았습니다.

 

1byte씩 파일을 읽어와 입출력하기 때문에 용량이 큰 파일을 입출력하면 속도가 상당히 느립니다.

출력값을 보면 1byte씩 읽고 쓴 것을 알 수 있습니다.

 

이를 보완하고 싶다면 메모리 버퍼를 사용해주면 됩니다.

2022.03.29 - [Java/Java] - 자바입출력/byte기반) 버퍼입출력스트림 (BufferedInputStream, BufferedOutputStream)