안녕, 세상!

10. 스트림과 파일 처리 본문

It공부/Java

10. 스트림과 파일 처리

dev_Lumin 2020. 7. 17. 00:10

(1) 스트림(Stream) 개념

스트림(Stream)은 파일이나 키보드, 모니터 등의 입출력 장치나 네트워크, 메모리 등으로부터 데이터를 입출력할 때 사용하는 처리를 정의해 둔 것입니다.

스트림은 하드웨어 장치와 무관하게 일관된 방법으로 이동되는 흐름입니다.

자바에서 모든 입출력에서 스트림이라는 개념을 사용합니다.

물이 필요할 때 저수지나 개울에서 물을 가져다 쓸 수 있는 것처럼, 필요한 바이트 문자를 파일이라는 데이터 저장소에서 가져온다고 보는 C언어와 유닉스에서 온 개념입니다.

이를 다시 비유 설명하자면 파일에 스트림이라는 파이프를 연결하면 프로그램으로 데이터를 가져올 수 있습니다.

FIFO(First-In-First-Out) 구조며 읽고 쓰는 처리를 동시에 할 수 없는 단방향 구조입니다.

 

 

스트림은 크게 2가지 종류로 나눌 수 있습니다.

(1) 노드 스트림 : 직접 파일에 연결되는 스트림

                       대표적인 입력 노드 스트림으로 InputStream 클래스가 있으며 System.in 도 이 클래스의 객체입니다.

 

(2) 필터 스트림 : 노드 스트림에 연결하여 다른 처리를 해주는 스트림

 

편의상 파일이라고 표현하지만 자바에서는 키보드, 모니터, 메모리, 네트워크 상의 리소스도 마찬가지로 취급합니다.

 

 

데이터의 저장소로부터 데이터를 하나 가져오면 노드 스트림을 통과하게 됩니다. 

이렇게 가져온 데이터는 순수한 바이트로서, 바로 사용하기엔 다소 문제가 있을 수 있습니다.

대표적인 문제로 한글이 있습니다.

보통 영문 2바이트가 한글 1글자이기 때문에 노드 스트림에서 나온 바이트를 화면에 출력하면 한글이 모두 깨지는 현상이 발생합니다.

그래서 노드 스트림에 필터 스트림을 연결합니다.

필터 스트림의 생성자에 노드 스트림의 객체를 주면, 두 개의 스트림이 연결됩니다.

영어 이외의 문자를 처리하는 대표적인 입력 필터 스트림은 리더(Reader)이고 출력 필터 스트림은 라이터(Writer)입니다.

노드 스트림과 필터스트림

만일 노드 스트림인 InputStream 클래스에서 생성된 System.in 객체를 리더인 InputStreamReader 클래스의 객체에 연결한다면 다음과 같습니다.

노드 스트림바이트(byte) 단위로 데이터를 읽고 쓰지만, 리더를 연결하면 캐릭터(문자) 단위로 데이터를 읽고 쓰게 됩니다.

입출력 단위 입력 스트림 출력 스트림
바이트(byte) InputStream OutputStream
캐릭터(char) Reader Writer

 

프로그램을 읽을 때 마다 노드 스트림을 통해서 파일 등에 접근하기 때문에 노드 스트림을 바로 사용할 때 지연이라는 문제점이 발생할 수밖에 없습니다.

이를 해결하기위해 필터 스트림인 버퍼를 사용합니다.

 

버퍼 입력 스트림

(1) BufferedReader 클래스

    InputStreamReaded 클래스(필터 스트림)와 같이 리더에 연결함

(2) BufferedInputStream 클래스

    InputStream 클래스(노드 스트림)에 바로 연결함

 

출력 스트림의 경우에는 BufferedWriter, BufferedOutputStream 클래스가 있습니다.

 

한글을 사용하는 우리나라와 같은 환경에서는 보통 BufferedReader 클래스를 사용합니다.

 

버퍼를 사용하면 프로그램에서 읽으라고 요청한 데이터보다 많은 데이터를 미리 읽어 놓고 다음번에 프로그램이 데이터를 요청하면 파일까지 가지 않고 메모리의 버퍼로부터 바로 읽기 때문에 성능이 더 좋아지게 됩니다.

 

BufferedReader 클래스와 InputStreamReader 클래스 연결은 InputStreamReader 클래스의 객체를 다음과 같이 사용하면 됩니다.

 

필터 스트림에는 이외에도 여러 가지 종류가 존재합니다.

각 노드 스트림에 연결할 수 있는 필터 스트림이 따로 있기 때문에 관계되는 노드 스트림과 필터 스트림을 구분할 줄 아는 것이 중요합니다.

 

 

 

문자 스트림(Character Stream)

(맨 뒤에 Reader/writer 붙임)

문자입력 스트림 : 바이트를 전달 받고, 이 바이트들을 '로컬 문자 집합'에 있는 문자인지 비교하여 문자로 변환

 

 

바이트 스트림(Byte stream)

(맨 뒤에 Stream 붙임)

    

 

 

(2) 입력 스트림과 리더

입력 스트림은 프로그램에서 필요로 하는 데이터를 외부 기억장치나 입력장치로부터 읽어 들일 때 사용하는 스트림입니다.

 

 

① Inputstream과 InputStreamReader

InputStream 클래스는 바이트 단위로 읽기 때문에 한글 등의 이유로 캐릭터 단위로 읽기 위해서는 InputStreamReader 클래스를 연결해서 사용해야 합니다.

InputStream 클래스는 추상 클래스이므로 반드시 상속받아서 사용하거나 InputStream 클래스의 서브클래스를 사용해야 합니다.

InputStream 클래스의 객체를 생성하는 것은 불가능하지만 자바에서 제공하는 System.in 객체를 사용하는 것은 가능합니다.

System.in 객체 : InputStream 클래스의 객체

 

InputStream 클래스가 다른 모든 입력 스트림의 슈퍼 클래스이기 때문에 InputStream 클래스의 메소드들 중 알고 있어야 하는 것들이 있습니다.

메소드 설명
int read(byte [ ] b) b 배열만큼 데이터를 입력
int read(char [ ] cbuf) cbuf 배열만큼 데이터를 입력
long skip(long n) n개 만큼 읽어 들일 데이터를 건너 뜀
void close() 스트림을 닫음(종료)

 

InputstreamReader는 바이트 스트림을 전달받아 문자정보로 변환합니다.

문자 정보로 변환할 때 디폴트로 MS949 한글 확장 완성형 문자 집합이 있기 때문에 한글이 깨지지 않습니다.

 

윈도우의 메모장에서 한글 텍스트를 입력하면 디폴트로 마이크로소프트에서 만든 '한글 확장 완성형 문자집합'

에 있는 코드로 저장하는데 윈도우에서 디폴트로 사용하는 문자 집합은 MS949 입니다.

 

InputStreamReader in = new InputStreamReader(fin, "MS949");

 

 

 

InputStream의 예제에 설명하기전에 read() 메소드에 대해서 설명하겠습니다.

read() 메소드는 단순하게 인풋 스트림에서 1byte() 만 읽는 메소드입니다.

입력장치인 키보드로부터 입력을 받고 입력받은 값을 1byte()만 저장합니다.

read()가 스트림 끝 혹은 파일 끝을 만나면 -1을 리턴하여 끝납니다.

 

System.in.read();

키보드로 'java'라고 입력을 했는데 결과가 'ava'만 출력된 이유는 System.in.read() 메소드가 알파벳 'j'를 읽고 가져갔기 때문입니다.

나머지인 'ava'를 System.in.read(input); 이 input 변수에 저장을 하여 출력해서 다음과 같은 결과가 나왔습니다.

이와 같이 System.in.read()를 통해서 read() 메소드는 1byte()를 읽고 저장한다는 것을 알 수 있습니다.

 

위의 코드에서 예외처리 IOException을 해주는 것은 자바에서 print(), println(), prinft() 메서드에만 자체적으로 예외처리를 했지만 이외의 입출력 메소드들은 자체적으로 예외처리가 되어 있지 않아서 예외처리를 해줘야 합니다.

위의 코드로 설명하자면 예외처리를 해주지 않는다면 System.in.read(); 는 오류가 날 것입니다.

예외처리를 try문과 catch문을 사용해서 해도 되지만 main 메소드 마지막에 'throws IOException' 이라고 추가로 써서 예외처리를 할 수도 있습니다.

 

read() 메소드는 지정한 배열 위치에서 지정한 길이만큼만 읽어서 저장시킬 수도 있습니다.

다음 결과에 앞의 공백 부분은 input 배열에 인덱스 3부터 값을 넣기로 설정했으므로 이전의 인덱스인 0,1,2는 비워져 있어서 스페이스바 3칸이 띄어진 상태에서 'ilovejava' 의 3byte 글자인 'ilo' 만 읽고 저장한 것입니다.

 

 

다음 예제는 InputStream 클래스의 객체인 System.in에 InputStreamReader 클래스의 객체를 연결하여 키보드로부터 문자열을 입력받는 프로그램입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.io.*;   // stream을 사용하기 위해서 
public class instreamsample {
    public static void main(String[] args) {
        InputStreamReader inr = new InputStreamReader(System.in);
        //한글 입력도 가능하게 하기 위해 InputStream의 객체 System.in과 InputStreamReader 연결
        try {
            while(true) {
                int i = inr.read();  //read()는 키보드로부터 입력을 받고 값으로 저장함
                //char myChar = (char)i;  //아스키 코드를 문자로 바꿔줌
                System.out.print(i+"\n");
            }
        }catch(IOException e) {
            System.out.println(e.toString());
        }
    }
}
cs

read() 메소드에 대해 조금 더 설명하기 위해서 9번째 줄은 주석처리로 해놨습니다.

키보드로부터 입력받은 값을 읽고 이것을 int 형 변수인 i에 값을 저장했기 때문에 i 에는 아스키코드가 저장될 것입니다.

키보드로부터 'a' 값을 치고 결과를 출력하려고 엔터를 치게 되면서 엔터의 아스키코드 13이 출력되고 엔터를 치면서 줄 바꿈이 실행돼서 줄 바꿈의 아스키코드인 10이 같이 출력된 것입니다.

read() 메소드는 1 byte만 읽고 가져간다고 했는데 값이 3개나 나온 이유는 while 문으로 무한루프를 돌고 있기 때문에 while 문이 a 이외에 엔터와 줄 바꿈의 아스키코드도 출력하게 된 것입니다.

 

8번째 줄에 read() 메서드의 값을 변수 char이 아닌 int 변수에 저장시킨 이유는 오로지 int 변수만 read() 메소드( 1 btye만 받는경우) 의 값을 받을 수 있기 때문입니다.

8번째 줄을 'char i' 라고 하면 오류가 뜹니다.

그래서 아스키코드 값으로 받고 해당 아스키코드 값을 다시 문자인 char 형식으로 바꿔주는 과정을 거치는 것입니다.

 

객체.read()의 타입을 int로 하는 이유!

read()가 스트림의 끝 혹은 파일의 끝을 만나면 -1을 리턴한다고 했습니다.

만약 읽으려는 값 자체가 -1이면 값을 의미하는 -1인지 리턴을 의미하는 -1인지 구분을 못할 것입니다.

이 둘을 구분하기 위해서 int형을 쓰는 것입니다.

읽으려는 값이 -1 일 경우 read는 0xFF를 리턴합니다. 

하지만 이 때 int 타입으로 사용했기 때문에 0xFF를 32비트의 0x000000FF로 리턴합니다.

이것은 -1이 아닙니다. -1은 0xFFFFFFFF 이기 때문에 값의 -1과 return의 -1을 구분할 수 있게 됩니다.

 

 

 

여기까지가 read() 메소드에 대한 설명이었고 다시 InputStreamReader에 대한 설명을 하기 위해 위의 코드의 9번째 줄 주석을 풀고 일부를 수정한 예제는 다음과 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.io.*;   // stream을 사용하기 위해서 
public class instreamsample {
    public static void main(String[] args) {
        InputStreamReader inr = new InputStreamReader(System.in);
        //한글 입력도 가능하게 하기 위해 InputStream의 객체 System.in과 InputStreamReader 연결
        try {
            while(true) {
                int i = inr.read();  //read()는 키보드로부터 입력을 받고 아스키코드 값으로 저장함
                char myChar = (char)i;  //아스키 코드를 문자로 바꿔줌
                System.out.print(myChar);
            }
        }catch(IOException e) {
            System.out.println(e.toString());
        }
    }
}
cs

다음 결과로 확인할 수 있듯이 InputStreamReader 클래스를 InputStream클래스에 연결해서 한글 글자도 깨지지 않고 잘 나옴을 확인할 수 있습니다.

 

InputStreamReader 클래스가 제 역할을 하고 있는 것을 확인하려면 InputStreamReader을 없애고 출력을 시켜보면 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.io.*;   // stream을 사용하기 위해서 
public class instreamsample {
    public static void main(String[] args) {
        //InputStreamReader inr = new InputStreamReader(System.in);
        //한글 입력도 가능하게 하기 위해 InputStream의 객체 System.in과 InputStreamReader 연결
        InputStream inr= System.in;
        try {
            while(true) {
                int i = inr.read();  //read()는 키보드로부터 입력을 받고 아스키코드 값으로 저장함
                char myChar = (char)i;  //아스키 코드를 문자로 바꿔줌
                System.out.print(myChar);
            }
        }catch(IOException e) {
            System.out.println(e.toString());
        }
    }
}
cs

위의 코드는 필터 스트림인 InputStreamReader이 없는 InputStream만 존재하는 경우입니다.

다음과 같이 한글을 입력할 경우에 한글이 깨졌음을 확인할 수 있습니다.

 

 

다른 방법으로 InputStreamReader이 제 역할을 하는 것을 보려면 while(true) 무한루프 반복문을 없애면 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.io.*;   // stream을 사용하기 위해서 
public class instreamsample {
    public static void main(String[] args) {
        InputStreamReader inr = new InputStreamReader(System.in);
        //한글 입력도 가능하게 하기 위해 InputStream의 객체 System.in과 InputStreamReader 연결
        try {
            //while(true) {
                int i = inr.read();  //read()는 키보드로부터 입력을 받고 아스키코드 값으로 저장함
                char myChar = (char)i;  //아스키 코드를 문자로 바꿔줌
                System.out.print(myChar);
            //}
        }catch(IOException e) {
            System.out.println(e.toString());
        }
    }
}
 
cs

while 문이 없어져서 무한 루프가 없어졌으므로 키보드로 한 단어만 입력할 수 있게 되었습니다.

우선 영어를 입력해보겠습니다.

위에서 설명했듯이 read() 메소드는 1byte 밖에 못 읽기 때문에 다음과 같이 'abcd' 의 1byte 인 'a' 만 나온 것을 확인할 수 있습니다.

이제 한글을 입력해보겠습니다.

한글은 2byte라서 1byte 밖에 못 읽는 read() 메소드로 글자가 깨져야 하는데 깨지지 않고 잘 나왔음을 알 수 있습니다.

이를 통해 InputStreamReader 클래스가 제 역할을 한다는 것을 확인할 수 있습니다.

 

 

 

 

 

② FileInputStream과 FileReader

FileInputStream 클래스는 InputStream 클래스의 서브클래스로서 파일로부터 바이트 단위로 데이터를 읽어 들이는 입력 스트림입니다.

FileInputStream 클래스의 생성자는 다음과 같습니다.

메소드 설명
FileInputStream(String name) 파일명이 name인 파일에서 입력 받는 FileInputStream
FileInputStream(File file) file 객체가 가리키는 파일에서 입력 받는 FileInputStream

 

FileReader 클래스는 FileInputstream 클래스의 객체에 연결해서 사용하는 것이 아니라 FileInputStream 클래스와 마찬가지로 파일로부터 캐릭터 단위로 데이터를 읽어 들이는 입력 스트림입니다.

다음은 FileReader 클래스의 생성자입니다.

메소드 설명
FireReader(String name)  파일명이 name인 파일에 연결된 FileReader
FileReader(File file) file 객체가 가리키는 파일에서 입력 받는 FileReader

 

다음은 FileReader 클래스를 이용해서 하드 디스크에 저장되어 있는 파일의 내용을 읽어 출력하는 예제입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.io.*;
public class filersample {
    public static void main(String [] args) {
        int i;
        
        try {
            FileInputStream in =new FileInputStream("c:///download/testjava.txt");
            
            while((i=in.read())!=-1) { // 1byte 씩 읽고 아스키코드 값으로 저장. 
                char c = (char)i;      // 아스키코드값을 문자로 자료형 변환
                System.out.print(c);
            }
        }catch(IOException e) {
            System.out.println(e.toString());
        }
    }
}
cs

testjava.txt 라는 텍스트 파일의 문자를 캐릭터 단위로 읽어서 결과를 출력한 것입니다.

깨진 글자들은 한글입니다.

 

FileInputStream 클래스를 이용해서 깨진 글자는 InputStreamReader 필터 스트림을 이용해서 인코딩 속성을 "UTF-8"로 설정해주면 한글이 잘 출력됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.io.*;
public class filersample {
    public static void main(String [] args) {
        int i;
        try {
        //    FileReader in =new FileReader("c:///download/testjava.txt");
            InputStreamReader ir = new InputStreamReader(new FileInputStream("c:///download/testjava.txt"),"UTF-8");
            while((i=ir.read())!=-1) {
                char c = (char)i;
                System.out.print(c);
            }
        }catch(IOException e) {
            System.out.println(e.toString());
        }
    }
}
 
cs

 

 

 

텍스트 파일을 읽을 때 FileReader 클래스가 주로 사용되고, 바이너리 파일을 읽을 때는 FileInputStream 클래스가 주로 사용됩니다.

 

 

 

③ BufferedInputStream과 BufferedReader

BufferedInputStream 클래스는 InputStream 클래스에 버퍼를 추가한 스트림입니다.

BufferedInputStream 클래스를 사용하면 프로그램에서 데이터를 읽어올 때 요청한 데이터보다 많은 양의 데이터를 미리 버퍼에 읽어 놓았다 다음 요청  때 돌려주기 때문에 프로그램이 기다리는 시간이 줄어들어서 전체적인 수행 속도가 빨라집니다.

 

BufferedInputStream 클래스는 필터 스트림이기 때문에 그 자체로는 쓸 수 없으며, InputStream 클래스의 객체를 인수로 받아서 생성해야 합니다.

마찬가지로 InputStreamReader 클래스에 버퍼를 추가한 것이 BufferedReader 클래스입니다.

 

BufferedReader 클래스를 생성할 때 InputStreamReader 클래스의 객체를 인수로 주면 버퍼를 사용하면서 캐릭터 단위로 데이터를 읽는 스트림이 생성됩니다.

 

다음은 BufferedReader 클래스로 키보드로부터 문자열을 읽는 예제입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
iimport java.io.*;
public class buffrsample {
    public static void main(String[] args) {
        InputStreamReader rd = new InputStreamReader(System.in);
        BufferedReader br = new BufferedReader(rd);
        try {
            String str = br.readLine(); //줄을 읽고 string 형태로 반환
            System.out.print(str);
        }catch(IOException e) {
            System.out.println(e.toString());
        }
    }
}
cs

InputStreamReader 을 사용했기 때문에 한글도 잘 나옵니다.

 

 

 

④ DataInputStream

필터 스트림 중에 바이트 단위, 캐릭터 단위로 읽는 InputStream 클래스와 InputStreamReader 클래스는 이외에 자바의 기본 데이터 형으로 읽을 경우에는 DataInputStream 클래스를 사용합니다.

DataInputStream 클래스는 필터 스트림이기 때문에 InputStream 클래스 객체를 인수로 받아 생성해야 합니다.

DataInputStream 클래스는 한글이 깨지는 대표적인 문제점과 더불어 여러 가지 문제를 일으킬 수 있기 때문에 특별한 경우 아니면 사용하지 않는 것이 좋습니다.

DataInputStream 클래스에서 제공하는 메소드들은 다음과 같습니다.

 

 

 

 

 

 

(2) 출력 스트림과 라이터

출력 스트림은 프로그램에서 외부 기억(출력) 장치에 데이터를 내보낼 때 사용하는 스트림입니다.

출력 스트림도 비 영어권 문자를 처리하기 위한 필터 스트림이 있는데, 이를 라이터(Writer)이라고 합니다.

 

 

① OutputStream과 OutputStreamWriter

(1) OutputStream 클래스

대표적인 출력 스트림으로 바이트 단위로 데이터를 출력합니다.

InputStream 과 마찬가지로 추상 클래스여서 바로 사용 못하고 서브클래스를 사용해야 합니다.

OutputStream 클래스는 모든 출력 스트림의 슈퍼 클래스이기 때문에 OutputStream의 모든 메소드는 서브 클래스에서 사용 가능합니다.

System.out은 OutputStream의 객체가 아니고 OutputStream의 서브 클래스인 PrintStream 클래스의 객체입니다.

 

(2) OutputStreamWriter 클래스

필터 스트림으로 캐릭터 단위로 문자를 출력합니다.

한글을 깨지지 않고 사용할 수 있게 합니다.

 

다음은 예제입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
import java.io.*;
public class outpsample {
    public static void main(String [] args) {
        OutputStreamWriter out = new OutputStreamWriter(System.out); //아웃풋스트림과 라이터 연결
        
        try {
            out.write("자바좋아!");
            out.flush(); // 출력
        }catch(IOException e) {
            System.out.println(e.toString());
        }
    }
}
cs

 

 

② FileOutputStream 과 FileWriter

(1) FileOutputStream 클래스

OutputStream 클래스의 서브 클래스로서 파일에 바이트 단위로 출력하는 스트림입니다.

메소드 설명
FileOutputStream(String name) 파일명이 name인 파일에서 출력하는 FileOutputStream
FileOutputStream(File file) file 객체가 가리키는 파일에서 출력하는 FileOutputStream

 

다음은 예제입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.io.*;
public class fileoutsample {
    public static void main(String [] args) {
        try {
            FileOutputStream out = new FileOutputStream("c:///download/testjava.txt");
                                            // 위의 파일에 출력하도록 
            FileInputStream fis = new FileInputStream("c:///download/testjava.txt");
            InputStreamReader in = new InputStreamReader(fis); 
            
            for(byte i =1;i<=5;i++) {
                out.write(97); //FileOutputStream의 객체의 write() 메소드는 아스키코드 숫자만 받음
            }
            int c;
            while((c=in.read())!=-1) { // 해당 파일의 값을 아스키 코드로 읽어와서 하나씩
                System.out.print(c+" ");  // 출력
            }
            
            in.close(); 
            out.close();
                    }catch(IOException e) {
            System.out.println("파일을 쓰거나 읽을 수 없습니다.");
        }
    }
}
cs

testjava.txt 파일에 FileOutputStream 클래스를 이용해 출력을 하고 저 값을 다시 FileInputStream 을 이용해서 입력을 했습니다.

write() 메소드를 사용할 때 아스키코드 숫자 97을 5번 반복문으로 출력했으므로 testjava.txt에 'a' 가 다섯 번 나온 것을 확인할 수 있습니다.

파일에서 글자가 나온 것을 확인하려면 우선 텍스트 파일이 꺼져있는 상태에서 코드를 실행시키고 파일을 열면 확인할 수 있습니다.

FileOutputStream 클래스와 FileInputStream 클래스를 초기화(정의) 하는 경우는 try { } 함수 안에 써야 오류가 나지 않습니다.

즉, 예외처리를 해야 됩니다.

 

 

 

 

(2) FileWriter 클래스

파일에 캐릭터 단위로 출력합니다.

FileReader과 마찬가지로 FileOutputStream 클래스에 연결해서 사용하는 것이 아닙니다.

메소드 설명
FireWriter(String name)  파일명이 name인 파일에 출력하는 FileWriter
FileWriter(File file) file 객체가 가리키는 파일에서 출력하는 FileWriter
FileWriter(String name, boolean apnd) 파일명이 name인 파일을 열어서 apnd 값에 따라 추가하거나 새로 출력하는 FileWriter

 

다음은 testjava1.txt 파일을 읽어서 testjava2.txt 파일에 쓰는 예제입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.io.*;
public class fwsample {
    public static void main(String[] args) {
        try {
            FileReader in = new FileReader("c:///download/testjava1.txt");
            FileWriter out = new FileWriter("c:///download/testjava2.txt");
            
            int c;
            while((c=in.read())!=-1) {
                out.write(c);
            }
            in.close();
            out.close();
            }catch(IOException e) {
                System.out.println(e.toString());
        }
    }
}
cs

결과가 잘 나왔지만 역시 위해서 봤듯이 FileReader 클래스로 읽으니 한글이 깨집니다.

한글이 깨지지 않을려면 5번째 줄을 다음과 같이 바꾸면 됩니다.

 

InputStreamReader in = new InputStreamReader(new FileInputStream("c:///download/testjava1.txt"),"UTP-8");

 

 

 

③ BufferedOutputStream과 BufferedWriter

BufferedOutputStream 클래스는 버퍼 처리를 수행하는 출력 스트림입니다.

BufferedOutputStream 클래스로 구성한 출력 스트림에 데이터를 출력하게 되면 출력 장치(모니터, 파일 등)에 바로 출력되는 것이 아니라 일단 메모리의 버퍼에 출력됩니다.

버퍼가 가득 차면 자동으로 출력 장치에 출력되기 때문에 프로그램이 매번 저장 장치에 기록되기를 기다리는 OutputStream에 비해 전체적인 수행 속도가 빨라집니다.

BufferedOutputStream 클래스는 필터 스트림이기 때문에 그 자체로 사용할 수 없고 OutputStream 클래스의 객체를 인수를 주어 생성해야 합니다.

마찬가지로 BufferedWriter 클래스를 생성할 때 OutputStreamWriter 클래스의 객체를 인수로 주면 버퍼를 사용하면서 캐릭터 단위로 출력하는 스트림이 생성됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.io.*;
public class buffwsample {
    public static void main(String[] args) {
        BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
        
        try {
            out.write("버퍼에 출력된 문자열");
            out.flush();  // 버퍼에 있는 데이터를 강제로 밀어냄 -> 출력
        }catch(IOException e ) {
            System.out.println(e.toString());
        }
    }
}
 
cs

위에서 설명했듯이 BufferedWriter 클래스는 출력장치에 바로 출력되는 것이 아니라 사이즈 별로 메모리의 버퍼에 출력한다고 설명했습니다. 

버퍼가 가득 채워지게 되면 모든 데이터를 한 번에 내보내도록 되어 있는데 

flush() 메소드는 버퍼에 데이터가 가득 차 있건 아니건, 버퍼에서 강제로 밀어내도록 하는 메소드입니다.

close() 메소드는 자바의 I/O 스트림을 닫아주는 메소드입니다.

따라서 보통 flush() 메소드를 호출하여 버퍼를 비우고 close() 메소드로 스트림을 닫아주는 식으로 사용합니다.

 

 

 

 

④ DataOutputStream

DataOutputStream 클래스는 자바의 기본 데이터 형으로 출력할 수 있도록 해주는 출력 스트림입니다.

DataOutputStream 클래스도 바이트 단위로 데이터를 처리하기 때문에 한글 출력 등 여러 문제가 발생할 수 있으므로 PrintStream 클래스나 PrintWriter 클래스를 사용하는 것이 바람직합니다.

 

 

 

 

⑤ PrintStream과 PrintWriter

PrintStream 클래스는 필터 스트림으로 OutputStream 클래스의 객체를 인수로 받아서 생성할 수 있습니다.

PrintStream 클래스는 outputStream 클래스의 모든 기능을 print()  메소드와 println() 메소드로 출력할 수 있도록 해주는 편리한 출력 스트림입니다.

 

printWriter 클래스는 OutputStreamWriter 클래스의 메소드들을 print() 메소드와 println() 메소드만으로 사용할 수 있게 정리한 필터 스트림입니다.

 

다음은 PrintStream 클래스의 주요 메소드입니다.

print()메소드와 println() 메소드는 메소드 오버로딩을 이용하여 주어지는 데이터 형에 상관없이 출력할 수 있도록 되어 있습니다.

 

다음 예제입니다.

1
2
3
4
5
6
7
8
import java.io.*;
public class printwsample {
    public static void main(String [] args) {
        PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
        out.print("자바 좋아!");  // write() 대신에 사용했다고 생각하면 됨
        out.flush();     // 버퍼에서 강제로 밀어내서 출력
    }
}
cs

여기서는 결과를 출력하는 역할을 하는 코드는 out.flush() 입니다.

print() 가 결과를 출력한다고 헷갈리지 맙시다.

 

 

 

 

 

 

(3) File 클래스

파일에 저장된 데이터를 읽거나 쓸 수 있었던 FileReader, FileWriter 클래스와 달리 파일의 이름의 변경, 삭제, 존재 확인 등 파일 그 자체를 다루고 싶을 사용하는 클래스입니다.

자바에서 파일 자체를 제어할 수 있도록 File 클래스를 제공하고 파일 이름을 인수로 주기만 하면 쉽게 객체를 생성할 수 있습니다.

다음은 File 클래스의 생성자입니다.

메소드 설명
File(String path) path 경로명에 해당하는 File
File(String path, String name) path 홀더에 존재하는 name의 File
File(File dir, String name) dir 객체와 같은 홀더에 존재하는 name이름의 file

 

File 클래스의 주요 메소드는 다음과 같습니다.

 

다음은 특정 파일에 대한 정보를 출력하는 예제입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.io.*;
public class fileclassample {
    public static void main(String [] args) {
        File my_file = new File("c:///download/testjava.txt");
        if(my_file.exists()) {
            System.out.println("파일 이름: "+my_file.getName());
            System.out.println("상대 경로: "+my_file.getPath());
            System.out.println("절대 경로: "+my_file.getAbsolutePath());
            System.out.println("쓰기 가능: "+my_file.canWrite());
            System.out.println("읽기 가능: "+my_file.canRead());
            System.out.println("파일: "+my_file.isFile());
            System.out.println("디렉터리: "+my_file.isDirectory());
            System.out.println("파일크기: "+my_file.length());
        }
        else
            System.out.println("파일이 존재하지 않습니다.");
    }
}
cs

 

 

 

 

'It공부 > Java' 카테고리의 다른 글

Eclipse 환경에서 JavaFX 사용하기 (JDK11 버전 이후)  (12) 2020.08.05
11. 네트워크 프로그래밍  (0) 2020.07.18
9. 다중 쓰레드  (0) 2020.07.16
8. 그래픽  (0) 2020.07.15
7. 이벤트 프로그래밍 (2)  (0) 2020.07.14
Comments