본문 바로가기

kh정보교육원 JAVA

Stream, FileOutputStream, FileInputStream

스트림에 대해 알아보자.

 

스트림 : IO : Input Output -> 입력, 출력을 할 수 있는 길

 

스트림은 데이터 전송 단위로 구분한다.

byte, char  바이트로 옮겨지냐 문자로 옮겨지냐(처리하냐)에 따라서 스트림이 달라진다.

byte, char 단위로 옮기는 스트림은 입력 스트림 출력 스트림 이있다. 

다만 여기서 byte 입력 스트림 출력 스트림은 InputStream, OutputStream

char 입력 스트림, 출력 스트림은 Reader, Writer 나뉘는 것이다.

 

이 네 가지는 InputStream OutputStream Reader Writer

IO 입출력의 최상위 클래스인데 추상화 클래스라서 객체화 하지는 못한다.

여기에 사용해야 할 기능들을 정의해 놓았을 뿐이다. ex read(), write() close() 등

 

그래서 내가 프로그램과 통신하고자하는 이름을 붙인 다양한 하위클래스가 존재하고 이를 객체화 해서 사용한다.

즉, 내가 입출력 하고자 하는 걸 앞에 붙여서 객체화 해주면 된다.

ex FileInputStream  FileOutputStream 

 

FileInputStream  FileOutputStream을 봤을 때 해석할 수 있어야한다. 

FileInputStream : 파일로부터 byte단위로 읽을 때 사용하는 스트림 (= 1byte 씩 입력받는 스트림)

FileOutputStream : 파일로 byte 단위로 저장할 때 사용하는 스트림 (= 1byte 씩 출력하는 스트림)

FileWriter : 텍스트 파일로 문자 단위로 저장할 때 사용하는 스트림

FileReader :  텍스트 파일로부터 문자 단위로 읽을 때 사용하는 스트림  

 

스트림은 단방향이므로 입출력을 동시에 할 수는 없다.

동시에 하고자 하면 입력 스트림과 출력 스트림을 둘 다 사용해야 한다.

 

FileOutputStream과 FileInputStream을 알아보자

 

**코드 참조**  : 코드에 대한 설명히 상세히 되어있으니 천천히 읽어보면 이해가 될 것이다. 

                     읽기 불편하면 코드 복사 후에 확인하길 바란다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package com.kh.chap02_byte.model.dao;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
 
public class FileByteTest {
 
    public void fileSave() {
        // OutputStream이란 내보낸다는 의미
        // 파일 저장 -> 프로그램으로부터 파일을 내보냄 -> 파일 출력
        // FIleOutputStream을 열자 -> 연다는 건 객체를 만드는 것
        File file = new File("sample.txt");
        FileOutputStream fout = null
// 먼저 선언해두는 이유 : (FileOutputStream) 길을 연 후 닫아주는 처리가 필요하기에
// finally에 fout.close해줘야 되는데  fout가 트라이 구문 안에 있기 때문에(블럭 안에 갇힘)
//지역변수로서 선언해주기 위해 위에다가 선언해두고 아래서 OutputStream으로 담아주는 것이다.     
    
        try {                           
            fout = new FileOutputStream(/* file */"byteTest.txt"); 
            // new FileOutputStream은 어떤 파일과의 출력 길을 연다는 건가?  
// -> 지금 내 프로그램과 생성자에 전달된 sample.txt 파일
            // -> 어떤 파일을 저장(=출력)할 건지 매개변수 생성자로 전달
            // 스트림 객체 생성 => 매개변수로 전달 된 파일과의 출력 스트림 생성 됨
 
            // 현재 내 프로젝트 안에 sample.txt, byteTest.txt 파일은 없음 But 파일이 없어도 내부적으로 생성 후 스트림 생성 됨 
            // 전달 된 파일이 없는 파일일 경우 내부적으로 생성 후 스트림 생성(열어줌)
            // -> 파일 없으면 만들어 주면 되는데 FileNotFoundException을 핸들링해줘야 하는 이유는 존재하지 않는 디렉토리 포함 되었을 경우
        
            // 출력은 write 메소드 통해서 처리가 됨 
            // 1. write (int b) : 주어진 값 쓰기 
            fout.write(65); // IOException 처리 필요 
            fout.write('B'); // 문자도 int로 바껴서 갈 수 있음 
            // 실행 후 IO프로젝트에서 refresh 해주면 byteTest.txt 파일이 생기는데, txt 곧 문자 파일이기 때문에 내가 전달한 값들이 문자로 표현된다. 
            
            // 2. write(byte[] b) : 주어진 배열에 저장 된 모든 내용 쓰기 
            byte[] bArr = {979899};   //해당 문자 : a b c 
            fout.write(bArr);
            // 실행 후 결과 : ABabc  (덮어씀)
            
            // 3. write(byte[] b, int off, int len)  : 주어진 배열에 저장 된 내용 중 off번째부터 len개 만큼 쓰기
            fout.write(bArr, 12);
            // 실행 후 결과 : ABabcbc
        
        }/* catch (FileNotFoundException e) { => 다형성 적용해서 생략도 가능 
            e.printStackTrace();
        } */catch (IOException e) {
            e.printStackTrace();
        } finally {    // IO관련 클래스는(FileOutputStream) 길을 연 후 닫아주는 처리가 필요 
            try {
                fout.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        // OutputStream이란 내보낸다는 의미이고 이걸 통해 파일로 저장한다. 
        // 파일로 저장하기 위해 FIleOutputStream을 열어주어야 한다. 열어주는 건 곧 객체 생성 new FileOutputStream을 말한다.
        // 파일을 출력 스트림을 열어주었으니 어떤 파일을 저장해 줄 건지(내보내 줄건지, 출력해 줄건지) 생성자에 적어주면 된다. 
        // 즉, FileOutputStream은 생성자에 전달한 파일과 출력 길을 만들어 주고
// 내가 write메소드를 이용해 전달한 값들을 생성자에 전달한 파일 쪽으로 기록(출력)해주는 기능
    }
 
   cs
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public void fileOpen() {
        // 파일을 읽어오는 건 실제로 존재하는 파일을 내 프로그램 상으로 가져오는 것  -> fileOpen
        // 출력 스트림을 사용한다. 어떠한 파일을 저장한다는 개념 
        
        //byteTest.txt에 담김 걸 읽어오자 
        
        
        FileInputStream fis = null;
        
        try {                    // 무슨 파일 읽어올 건지 전달 
            fis = new FileInputStream("byteTest.txt");  // 입력 스트림을 연다 = 객체 생성 : 생성자를 통해 어떤 파일과 연결하는 처리해주면 연결 됨 
            // 1. int read() : 1byte(0~255)를 읽어오며 더 이상 읽어올 값이 없으면 -1 반환   -> 첫번째 byte를 읽어서 정수값으로 리턴
            // 2. int read(byte[] b) : 배열 b의 크기만큼 읽어서 배열을 채우고 읽어온 데이터의 수 반환
            // 3. int read(byte[] b, int off, int len)
            // : 최대 len개의 byte를 읽어서 배열 b의 지정 된 위치(off)부터 저장
              
            int value = 0;  // 읽어온 값인 read()를 읽어오는 조건으로만 쓰면 안 되고 저장을 해야 함
                            // 즉, 읽어와서 저장 후 그 값이 -1인지 아닌지 확인 후 출력
            while((value = fis.read()) != -1) {
                System.out.print((char)value);   // 그리고 int 타입으로 리턴되니 char타입으로 변환하면 byte.txt안에 write한 결과를 그대로 읽어옴 
            }
            
            
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    // 사실 예시를 보여준 것이지 바이트 단위 입출력은 문자에는 적절하지 않다.
// 문자를 입력하기 위에 바이트 단위로 입력하기 때이다.
문자 입출력 보다는 이미지 파일 등이 적절하다.
}
 
   cs

 

 

FileInputStream

FilleInputStream은 InputStreaam 앞에 File이 붙은 것으로 외부 자원인 파일을 내 프로그램 안으로 읽어오는 것이다. 

파일을 읽어오는 건 실제로 존재하는 파일을 내 프로그램 상으로 바이트 단위로 가져오는 것이다. 

읽어오기 위해 입력 스트림 즉, 입력할 길을 열어주어야 한다. 

Stream을 열어준다는 건 객체를 생성한다는 것이다. -> FileInputStream fis = new FileInputStream();

기본생성자로 두면 에러가 나는데 그 이유는 객체를 생성해서 파일로부터 입력할 길을 열었으면

어떤 파일을 읽어올 것인지 생성자 안에 적어주어야 하기 때문이다. 

=> 입력 스트림을 연다 = 객체 생성  -> 생성자를 통해 읽어올 파일과 연결 

FilIntputStream은 생성자에 전달한 파일을 가져와 읽어오기 위한 길을 만들어 주고

내가 read메소드를 통해 읽어온 값들을 생성자에 전달한 파일 쪽으로 입력해주는 기능이다.

 

FileOutputStream

FileOutputStream은 OutputStream 앞에 File이 붙은 것으로 내 프로그램에서 외부로 파일을 저장하는 것이다.

(파일을 저장한다 = 내 프로그램 안에서 파일을 만들어낸다( 내보낸다= 출력한다))

 

OutputStream이란 내보낸다는 의미이고 이걸 통해 파일로 저장한다. 
파일로 저장하기 위해 스트림 곧 길이 필요하니 FIleOutputStream을 열어주어야 한다.

열어주는 건 곧 객체 생성 new FileOutputStream을 말한다.
파일을 출력 스트림을 열어주었으니 어떤 파일을 저장해 줄 건지(출력해 줄건지) 생성자에 적어주면 된다. 
FileOutputStream은 생성자에 전달한 파일과 출력 길을 만들어 주고

내가 write메소드를 이용해 전달한 값들을 생성자에 전달한 파일 쪽으로 기록(출력)해주는 기능이다.

 

FileInputStream FileOutputStream 

스트림을 여는 순간 FileNotFoundException 이 발생할 수 있다는 걸 핸들링해줘야 한다.

내 기준 프로젝트 안에 byteTest.txt 파일은 없다. 그러나 파일이 없어도 내부적으로 생성 후 스트림이 생성이 된다.  
그렇다면 왜  FileNotFoundException을 왜 핸들링 해줘야 하냐면  존재하지 않는 디렉토리 포함 되었을 경우가 있기 때문이다. 

 

외부에서 내 프로그램으로 무언가 읽어오고자 한다면 입력 스트림을 열어서 read()로 읽어오면 되고

내 프로그램에서 무언가 외부로 출력하고자 한다면 출력 스트림 열어서 write()로 쓰면된다.

스트림을 열고 처리할 때 필요한 try catch구문을 잘 처리해주면 된다. 

 

Stream의 종류가 많아 헷갈릴 수 있지만 이름을 통해 어떤 일을 하는 Stream인지 이해하면 내가 필요한 Stream을 읽고 쓰는 건 어렵지 않다. 왜냐하면 읽을 때는 read 메소드로 읽으면 되고 출력을 한다면 write 메소드로 쓰면 된다. 물론 매read write의 매개변수는 다르지만 읽고 쓰는 행위는 같다.