자바공부<7> - JAVA IO

Posted by lib oimb
2018. 7. 24. 18:48 JAVA



1. 자바에서의 입출력


Input/Output (I/O)  , 입출력에 대해서 알아보자


입출력이란 내부적 또는 외부적으로 프로그램간 데이터를 주고 받는것 이라고 볼수 있다.

이 데이터를 주고 받기 위해서는 무언가가 필요한데 이것을 스트림(stream)이라고 한다. 한마디로 가상의 연결 통로라고 생각하면 된다.

이 스트림은 단방향 이기 때문에 통신을 위해선 두개의 스트림이 필요한데 하나는 입력 그리고 출력 이다.




예를 들면 파일과이 입출력시  그림과 같은 과정을 거친다.




2. 바이트 기반 스트림 과 문자 기반 스트림 그리고 보조 스트림


스트림의 종류에는 입력 스트림과 출력 스트림이 있다고 했다 

이번에는 이 스트림에 들어가는 종류를 알아 보자


스트림 내부에 들어갈 수 있는 단위로는 바이트와 문자가 있다

먼저 바이트 단위로 스트림을 구성하는 것을 알아보자




2.1 바이트기반 스트림 - InputStream , Outputstream


바이트 기반 스트림은 바이트 단위(1byte)로 데이터를 전송하고 받는다.

바이트 기반 스트림 입출력으로 나눠서 정리 하자면


파일

FileInputStream - FileOutputStream        


메모리(byte 배열)

ByteArrayInputStream - ByteArrayOutputStream


프로세스간 통신

PipedInputStream - PipedOutputStream


오디오 장치

AudioInputStream - AudioOutputStream


이 있는데 이 중 사용 목적에 따라 알맞게 사용 하면된다.

그리고 이들은 모두 InputStream , Outputstream 의 자식 클래스이다.

그리고 위의 두 부모 클래스는 입출력과 관련된 매서드가 존재한다

입력과 관련된 read() 매서드

출력과 관련된 write() 매서드 이다. 이 두매서드에 대해서는 JAVA api 에 잘 정리되어 있으므로 참고하는것이 좋다.(https://docs.oracle.com/javase/7/docs/api/)




2.2 문자기반 스트림 - Reader , Writer


자바의 경우 한 문자를 1바이트로 보지 않는다. 즉 cahr 형이 1byte가 아니다. 이 때문에 문자를 바이트 기반 스트림으로 읽거나 쓸경우 문제가 발생할 수 있다. 따라서 문자 기반 스트림을 사용해야 한다.


문자 기반 스트림도 사용되는 방법은 바이트기반과 비슷하다 다만 함수의 이름만 조금 바꿔진다.


파일

FileReader - FileWriter


char배열

CharArrayReader - CharArrayWriter


프로세스간 통신

PipedReader - PipedWriter



2.3 보조 스트림


위의 언급된 스트림 외에 스트림의 기능을 보완하기 위한 보조스트림이 있다.

보조 스트림은 실제 데이터를 주고받는 스트림이 아니기 때문에 데이터를 입출력할 수 있는 기능은 없지만, 스트림의 기능을 향상시키거나 새로운 기능을 추가할 수 있다. 

보조 스트림 역시 바이트기반과 문자기반으로 나뉘어지며 사용방식은 동일 하다.


Filter

Buffered

Print

Pushback

Data


등 뒤에 In(Out)putStream 과 Reader&Writer를 붙이면 된다.



다음으로 이제 하나씩 자세하게 살펴보자






3. 바이트 기반 스트림


바이트 기반 스트림 관련 클래스의 공통적인 매서드 중에서 필요한것 몇개를 뽑아보자면


InputStream 계열

intavailable()
Returns an estimate of the number of bytes that can be read (or skipped over) from this input stream without blocking by the next invocation of a method for this input stream.
abstract intread​()
Reads the next byte of data from the input stream.
intread​(byte[] b)
Reads some number of bytes from the input stream and stores them into the buffer array b.
intread​(byte[] b, int off, int len)
Reads up to len bytes of data from the input stream into an array of bytes.
voidclose​()
Closes this input stream and releases any system resources associated with the stream.


OutputStream 계열

voidwrite​(byte[] b)
Writes b.length bytes from the specified byte array to this output stream.
voidwrite​(byte[] b, int off, int len)
Writes len bytes from the specified byte array starting at offset off to this output stream.
abstract voidwrite​(int b)
Writes the specified byte to this output stream.
voidclose​()
Closes this output stream and releases any system resources associated with this stream.
voidflush​()
Flushes this output stream and forces any buffered output bytes to be written out.


이 정도가 있다  (참고) https://docs.oracle.com/javase/9/docs/api/index.html?overview-summary.html




3.1 ByteArrayInputStream과 ByteArrayOutputStream 


두 스트림은 바이트배열에 데이터를 입출력하는데 사용되는 스트림이다.


ByteArrayInputStream​(byte[] buf)
Creates a ByteArrayInputStream so that it uses buf as its buffer array.
ByteArrayInputStream​(byte[] buf, int offset, int length)
Creates ByteArrayInputStream that uses buf as its buffer array.


ByteArrayOutputStream​()
Creates a new byte array output stream.
ByteArrayOutputStream​(int size)
Creates a new byte array output stream, with a buffer capacity of the specified size, in bytes.


두 생성자에는 차이가 있는데 , 스트림 생성시 버퍼를 받느냐의 차이가 있다.

이는 두 스트림의 생성시에 있어 차이가 있기 때문인데


ByteArrayInputStream contains an internal buffer that contains bytes that may be read from the stream. An internal counter keeps track of the next byte to be supplied by the read method.

즉 내부 버퍼를 '포함하여' 생성 된다 ( 내부 버퍼를 포함한다는 뜻은 내부적으로 버퍼를 생성해낸다는 뜻이아니다)


ByteArrayOutputStream class implements an output stream in which the data is written into a byte array. The buffer automatically grows as data is written to it. The data can be retrieved using toByteArray() and toString().

즉 자동으로 커진다는 것을 알수 있다. 그리고 이후 Byte 또는 String형태로 반환 할 수 있다고한다.

byte[]toByteArray​()
Creates a newly allocated byte array.
StringtoString​()
Converts the buffer's contents into a string decoding bytes using the platform's default character set.


이를 짧게 사용해보면

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
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.Arrays;
 
public class Test06 {
 
    public static void main(String[] args) {
        byte[] in = {0,1,2,3,4,5};
        byte[] out =null;
        
        ByteArrayInputStream input;
        ByteArrayOutputStream output;
        
        input = new ByteArrayInputStream(in);
        output = new ByteArrayOutputStream();
        
        int data =0;
        while ((data=input.read())!=-1){
            output.write(data);
            
        }
        
        out = output.toByteArray();
        
        System.out.println("Output : " + Arrays.toString(out));
    }
}
 
cs





3.2 FileInputStream 과 FileOutputStream을 알아보자


두 스트림은 이름과 같이 파일과의 입출력을 위한 스트림이다.


FileInputStream​(File file)
Creates a FileInputStream by opening a connection to an actual file, the file named by the File object file in the file system.
FileInputStream​(String name)
Creates a FileInputStream by opening a connection to an actual file, the file named by the path name name in the file system.


FileOutputStream​(File file)
Creates a file output stream to write to the file represented by the specified File object.
FileOutputStream​(File file, boolean append)
Creates a file output stream to write to the file represented by the specified File object.
FileOutputStream​(String name)
Creates a file output stream to write to the file with the specified name.
FileOutputStream​(String name, boolean append)
Creates a file output stream to write to the file with the specified name.


이 정도는 알고있어야 될것 같다. 간단하게 인자로 파일 객체를 주는 방법과 스트팅 형태를 직접주는 방법이 있다고 보면된다. 그리고 append는 계속 쓸것인지 아니면 덮어 쓸것인지로 이해하면 된다.


이 역시 코드로 살펴 보면


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
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
 
public class Test06 {
 
    public static void main(String[] args) {
    
        try {
            File f = new File("fileTest.txt");
            f.createNewFile();
            File f2 = new File("copyTest.txt");
            f2.createNewFile();
            
            FileInputStream fin = new FileInputStream(f);
            FileOutputStream fout = new FileOutputStream(f2,true);
            
            int data=0;
            while((data=fin.read())!=-1) {
                fout.write(data);
            }
            fin.close();
            fout.close();
                        
        } catch (IOException e) {
            System.out.println("파일 여는데 실패함");
        }    
        
    }
}
cs


이런식으로 쓸수 있겠다.



3.3 바이트기반의 보조스트림


이번에는 바이트 기반의 보조 스트림을 좀더 알아보자. 먼저 FilterIn(Out)... 은 모든 보조스트림의 조상이다. 앞서 말했듯이 보조스트림은 스스로

파일 입출력을 수행할 수 없다. 때문에 앞서 말했던 기반 스트림이 필요하다.

그리고

protectedFilterInputStream​(InputStream in)
Creates a FilterInputStream by assigning the argument in to the field this.in so as to remember it for later use.
생성자가 protercted이므로 인스턴스는 생성할 수없다 즉 이를 상속하는 클래스들을 이용해야한다. 상속하는 클래스는 위에서 언급한 클래스들을 이용하면 된다.



하나씩 알아보면


BufferedInputStream 과 BufferedOutputStream 이 있다.

두 스트림은 입출력의 효율을 높여주기 위해 버퍼를 제공하는 보조 스트림이다.


하나씩 살펴보면

When the BufferedInputStream is created, an internal buffer array is created. As bytes from the stream are read or skipped, the internal buffer is refilled as necessary from the contained input stream, many bytes at a time. 

스트림이 생성되면 내부버퍼 배열도 생성이 된다. 그리고 스트림으로 부터 바이트가 읽어나 넘겨지면 입력스트림에서 다시 채워진다. 

BufferedOuputStream class implements a buffered output stream. By setting up such an output stream, an application can write bytes to the underlying output stream without necessarily causing a call to the underlying system for each byte written.

출력스트림을 구현하는데 , 여기서 이 이 보조스트림이의 기본스트림(InputStream) 호출없이 이 보조스트림으로 바이틀 쓸수 있다는 말이다.


그리고 각각 생성자를 보면

BufferedInputStream​(InputStream in)
Creates a BufferedInputStream and saves its argument, the input stream in, for later use.
BufferedInputStream​(InputStream in, int size)
Creates a BufferedInputStream with the specified buffer size, and saves its argument, the input stream in, for later use.
BufferedOutputStream​(OutputStream out)
Creates a new buffered output stream to write data to the specified underlying output stream.
BufferedOutputStream​(OutputStream out, int size)
Creates a new buffered output stream to write data to the specified underlying output stream with the specified buffer size.



각각 스트림을 인자로 받아서 구현이 되어진다.

코드를 보면


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
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
 
public class Test06 {
 
    public static void main(String[] args) {
 
        try {
            File f = new File("fileTest.txt");
            f.createNewFile();
            File f2 = new File("copyTest.txt");
            f2.createNewFile();
 
            FileInputStream fin = new FileInputStream(f);
            
 
            BufferedInputStream bis = new BufferedInputStream(fin);
            if (bis.markSupported()) {
                int data = 0, count = 0;
                int half = (int) f.length() / 2;
 
                while ((data = bis.read()) > 0) {
                    System.out.println((char) data);
                    if (++count == half + 1) {
                        System.out.println("::"+count);
                        bis.mark(half);
                        System.out.println("마킹");
                    }
                }
                System.out.println("-------------------------");
 
                bis.reset();
                System.out.println("다시");
                while ((data = bis.read()) > 0) {
                    System.out.println((char) data);
                }
            }
            bis.close();
 
        } catch (IOException e) {
            System.out.println("파일 여는데 실패함");
        }
 
    }
}
 
cs


 mark ,reset을 제외하고 크게 보면 그냥 버퍼를 통해서 파일내용을 꺼내 출력하는 것이다. 다만 mark와 reset에 대해서 한번 공부해보길 바라는 마음으로 넣어 밨다.


DataInputStream 과 DataOutputStream


이 두 보조 스트림은 DataInput인터페이스와 DataOutput 인터페이스를 구현했다. 즉 , 데이터를 읽고 쓰는데 있어 byte단위가 아니라 기본 자료형의 단위로 읽고 쓸 수 있다는 장점이 있다. 그리고 출력형식이 16진수이므로 이를 텍스트파일로 출력해도 바로 눈으로 확인할 수는 없을 것이다.


각각 생성자는 소개 하지만 매서드는 정말 많으므로 반드시 api를 참고해야 될거같다.(https://docs.oracle.com/javase/9/docs/api/java/io/DataInputStream.html)


DataInputStream​(InputStream in)
Creates a DataInputStream that uses the specified underlying InputStream.


DataOutputStream​(OutputStream out)
Creates a new data output stream to write data to the specified underlying output stream.


이에 대한 코드는 생략하겠다. 단순하게 해당 매서드의 특성에 대한 값을 뽑거나 출력한다고 생각하면된다.





출처: http://myeonguni.tistory.com/743


4. 문자기반 스트림


바이트기반 스트림과 비슷하지만 다만 문자데이터를 다루는데 사용된다는 점을 제외하면 똑같다.

InputStream  ->  Reader  //  OuputStream  ->  Writer 로 변하며 매서드에서 byte배열에서 char형 배열을 사용한다는 점을 제외하곤 전부 같다

다만 주의해야 할점이 있는데 char형으로 읽는다고해서 단순하게 2바이트로 처리한다고 생각해서는 안된다 각각에 맞는 인코딩 방식이 있고

자바가 그것을 처리해준다고 생각해야된다.



4.1 FileReader 와 FileWriter


FileReader​(File file)
Creates a new FileReader, given the File to read from.
FileReader​(String fileName)
Creates a new FileReader, given the name of the file to read from.


FileWriter​(File file)
Constructs a FileWriter object given a File object.
FileWriter​(File file, boolean append)
Constructs a FileWriter object given a File object.
FileWriter​(String fileName)
Constructs a FileWriter object given a file name.
FileWriter​(String fileName, boolean append)
Constructs a FileWriter object given a file name with a boolean indicating whether or not to append the data written.


생성자를 보면 알 수있듯이 Stream과 다름 없다


이외에도 추가로 Piped... , String... 등이 있는데 쓰임새만 다를 뿐 내용은 같다.


이어서 문자 기반 보조스트림을 소개하겠다.



4.2 문자기반의 보조 스트림


가장 먼저 소개할 내용은 BufferdReader와 BufferedWriter 이다. 앞서 말한 BufferedIn(Out)Stream과 같은 장점을 갖고 있다. 다만 추가적인 매서드 정도는 소개해야 될거같다.

먼저 생성자를 보자


BufferedReader​(Reader in)
Creates a buffering character-input stream that uses a default-sized input buffer.
BufferedReader​(Reader in, int sz)
Creates a buffering character-input stream that uses an input buffer of the specified size.


BufferedWriter​(Writer out)
Creates a buffered character-output stream that uses a default-sized output buffer.
BufferedWriter​(Writer out, int sz)
Creates a new buffered character-output stream that uses an output buffer of the given size.

생성자를 보면 역시 형식은 똑같다.


다만 기능적으로 라인단위로 읽고 쓸수있는 매서드가 있다.

BufferdReader 매서드

StringreadLine​()
Reads a line of text.

BufferedWriter 매서드


voidnewLine​()
Writes a line separator.

이 2가지는 유용하게 쓰이므로 알아두면 좋다.



다음으로 바이트 기반스트림을 문자기반 스트림으로 연결시켜주는 보조 스트림이 있다.

InputStreamReader 와 OutputStreamWriter 이다.


InputStreamReader​(InputStream in)
Creates an InputStreamReader that uses the default charset.
InputStreamReader​(InputStream in,String charsetName)
Creates an InputStreamReader that uses the named charset.
InputStreamReader​(InputStream in, Charset cs)
Creates an InputStreamReader that uses the given charset.
InputStreamReader​(InputStream in,CharsetDecoder dec)
Creates an InputStreamReader that uses the given charset decoder.


InputStream을 인자로 받으며 여기서 디코딩 방식을 정할수 있다. 다만 문자열로 지정할 것인지 객체를 넣어줄것이지 결정할 뿐 똑같다.


------

OutputStreamWriter​(OutputStream out)
Creates an OutputStreamWriter that uses the default character encoding.
OutputStreamWriter​(OutputStream out,String charsetName)
Creates an OutputStreamWriter that uses the named charset.
OutputStreamWriter​(OutputStream out,Charset cs)
Creates an OutputStreamWriter that uses the given charset.
OutputStreamWriter​(OutputStream out,CharsetEncoder enc)
Creates an OutputStreamWriter that uses the given charset encoder.


OutputStream 을 인자로 받으며 역시 인코딩 방식을 지정해줄 수 있다.


사실 쓰임새는 똑같기 때문에 설명은 생략하고 코드를 보자면

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
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
 
public class Test06 {
 
    public static void main(String[] args) {
 
        try {
            FileInputStream fis = new FileInputStream("fileTest.txt");
            InputStreamReader isr = new InputStreamReader(fis);
            int data;
            while ((data = isr.read()) != -1) {
                System.out.println((char) data);
            }
            isr.close();
 
        } catch (IOException e) {
            System.out.println("파일 존재하지 않음");
        }
 
    }
}
 
cs



이상 입출력 공부를 마치겠습니다.

이 댓글을 비밀 댓글로
    • 호이짜
    • 2018.07.29 00:16
    똑같은 기능을 하는 여러가지 메소드들이 있지만 , 사실 어떤 경우에 어떤걸 쓰면좋은지 요즘에는 막개발하는 사람들이 많습니다.
    하지만, 필요한 파트에 그걸 알고 쓰는게 매우 중요합니다.

    개인적으로 SOCKET통신의 BYTE처리에 대해서 공부해보심 더 좋습니다.