본문

입출력, 스트림(Stream)

반응형

# 입출력(Input/Output)

I/O란 Input과 Output의 약자로 입력과 출력, 간단히 줄여서 입출력이라고 한다. 입출력은 컴퓨터 내부 또는 외부의 장치와 프로그램간의 데이터를 주고받는 것을 말한다.


# 스트림(Stream)

자바에서 입출력을 수행하려면, 즉 어느 한쪽에서 다른 쪽으로 데이터를 전달하려면, 두 대상을 연결하고 데이터를 전송할 수 있는 무언가가 필요한다 이것을 스트림(Stream)이라고 정리했다.

(참고 : 스트림은 TV와 VCR을 연결하는 입력선과 출력선 같은 역할을 한다.)


" 스트림이란 데이터를 운반하는데 사용되는 연결통로이다. "


스트림은 연속적인 데이터의 흐름을 물에 비유해서 붙여진 이름인데 여러 가지로 유사한점이 많다. 물이 한쪽 방향으로만 흐르는 것과 같이 스트림은 단방향통신만 가능하기 때문에 하나의 스트림으로 입력과 출력을 동시에 처리할 수 없다.

그래서 입력과 출력을 동시에 수행하려면 입력을 위한 입력스트림(input stream)과 출력을 위한 출력스트림(output stream),

모두 2개의 스트림이 필요하다.



- 입출력 메서드 사용법

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public abstract class InputStream {
    ...
    // 입력스트림으로 부터 1byte를 읽어서 반환한다. 읽을 수 없으면 -1을 반환한다.
    abstract int read();
 
    // 입력스트림으로부터 len개의 byte를 읽어서 byte배열 b의 off위치부터 저장한다.
    int read(byte[] b, int off, int len) {
        ...
        for(int i = off; i < off+ len; i++) {
            // read()를 호출해서 데이터를 읽어서 배열을 채운다.
            b[i] = (byte)read();
        }
        ...
    }
 
    // 입력스트림으로부터 byte배열 b의 크기만큼 데이터를 읽어서 배열 b애 저장한다.
    int read(byte[] b) {
        return read(b, 0, b.length);
    }
    ...
}
 
cs



# 보조스트림

스트림의 기능을 보완하기 위한 보조스트림이 제공된다. 보조스트림은 실제 데이터를 주고받는 스트림이 아니기 때문에 데이터를 입출력할 수 있는 기능은 없지만, 스트림의 기능을 향상시키거나 새루운 기능을 추가할 수 있다. 그래서 보조스트림만으로는 입렬력을 처리할 수 없고, 스트림을 먼저 생성한 다음에 이를 이용해서 보조스트림을 생성해야한다.


// 먼저 기반스트림을 생성한다.

FileInputStream fis = new FileInputStream("test.txt");

// 기반스트림을 이용해서 보조스트림을 생성한다.

BufferedInputStream bis = new BufferedInputStream(fis);


// 보조스트림인 BufferedInputStream으로부터 데이터를 읽는다.

bis.read();


코드 상으로는 보조스트림인 BufferedInputStream이 입력기능을 수행하는 것처럼 보이지만 실제 입력기능은 BufferedInputStream과 연결된 FileInputStream이 수행하고, 보조스트림인 BufferedInputStream은 버퍼만을 제공한다.


버퍼를 사용한 입출력과 사용하지 않은 입출력간의 성능차이는 상당하기때문에 대부분의 경우에 버퍼를 이용한 보조스트림을 사용한다.



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

지금까지 알아본 스트림은 모두 바이트기반의 스트림이었다. 바이트기반이라 함은 입출력의 단위가 1byte라는 뜻이다. 

이미 알고 있는 것과 같이 C언어와 달리 Java에서는 한 문자를 의미하는 char형이 1byte가 아니라 2byte이기 때문에 바이트기반의 스트림으로 2byte인 문자를 처리하는 데는 어려움이 있다.|

이 점을 보완하기 위해서 문자기반의 스트림이 제공된다. 

문자데이터를 입출력할 때는 바이트기반 스트림 대신 문자기반 스트림을 사용하자.


InputStream -> Reader

OutputStream -> Writer


바이트기반 스트림과 문자기반 스트림의 비교

바이트기반 스트림

문자기반 스트림 

 FileInputStream

 FileOutputStream

 FileReader

 FileWriter

 ByteArrayInputStream
 ByteArrayOutputStream

 CharArrayReader
 CharArrayWriter 

 PipedInputStream

 PipedOutStream

 PipedReader
 PipedWriter

 StringBufferInputStream
 StringBufferOutputStream

 (더이상 사용하지 않음)

 StringReader
 StringWriter 


스트림의 종류가 달라도 읽고 쓰는 방법은 동일하므로 아래의 예제들을 통해서 스트림에 읽고 쓰는 방법을 잘 읽혀두자.


- 바이트기반 스트림

ByteArrayInputStream과 ByteArrayOutputStream

ByteArrayInputStream과 ByteArrayOutputStream은 메모리, 즉 바이트배열에 데이터를 입출력 하는데 사용되는 스트림이다

주로 다른 곳에 입출력하기 전에 데이터를 임시로 바이트배열에 담아서 변환 등의 작업을 하는데 사용된다.


int read(byte[] b, int off, int len)와 void write(byte[] b, int off, int len)를 사용해서 입출력하는 방법을 보여주는 예제이다.


Source01) InputOutputEx01.java

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
package inputOutput;
 
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.Arrays;
 
public class InputOutputEx01 {
 
    public static void main(String[] args) {
        byte[] inSrc = { 0123456789 };
        byte[] outSrc = null;
 
        byte[] temp = new byte[10];
 
        ByteArrayInputStream input = null;
        ByteArrayOutputStream output = null;
 
        input = new ByteArrayInputStream(inSrc);
        output = new ByteArrayOutputStream();
 
        // 읽어 온 데이터를 배열 temp에 담는다.
        input.read(temp, 0, temp.length);
        
        // temp[5]부터 5개의 데이터를 write한다.
        output.write(temp, 55);
 
        outSrc = output.toByteArray();
 
        System.out.println("Input Source : " + Arrays.toString(inSrc));
        System.out.println("temp : " + Arrays.toString(temp));
        System.out.println("Output Source : " + Arrays.toString(outSrc));
 
    }
 
}
cs

Result)

1
2
3
Input Source : [0123456789]
temp : [0123456789]
Output Source : [56789]
cs


Source02) InputOutputEx02.java

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
package inputOutput;
 
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
 
public class InputOutputEx02 {
 
    public static void main(String[] args) {
        byte[] inSrc = { 0123456789 };
        byte[] outSrc = null;
 
        byte[] temp = new byte[4];
 
        ByteArrayInputStream input = null;
        ByteArrayOutputStream output = null;
 
        input = new ByteArrayInputStream(inSrc);
        output = new ByteArrayOutputStream();
 
        try {
            while (input.available() > 0) {
                // 읽어 온 데이터의 개수를 반환한다.
                int len = input.read(temp);
                // 읽어 온 만큼만 write한다.
                output.write(temp, 0, len);
            }
 
        } catch (IOException e) {}
 
        outSrc = output.toByteArray();
 
        System.out.println("Input Source : " + Arrays.toString(inSrc));
        System.out.println("temp : " + Arrays.toString(temp));
        System.out.println("Output Source : " + Arrays.toString(outSrc));
    }
 
}
cs

Result)

1
2
3
Input Source : [0123456789]
temp : [8967]
Output Source : [0123456789]
cs


- 문자기반 스트림

FileReader와 FileWriter

Source01) InputOutputEx03.java

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
package inputOutput;
 
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
 
public class InputOutputEx03 {
 
    public static void main(String[] args) {
        try {
            String fileName = "IOtest.txt";
            FileInputStream fileInputStream = new FileInputStream(fileName);
            FileReader fileReader = new FileReader(fileName);
            
            int data = 0;
            
            // FileInputStream을 이용해서 파일내용을 읽어 화면에 출력한다.
            while ((data=fileInputStream.read())!=-1) {
                System.out.print((char)data);
            }
            System.out.println();
            fileInputStream.close();
            
            
            // FileReader를 이용해서 파일내용을 읽어 화면에 출력한다.
            while ((data=fileReader.read())!=-1) {
                System.out.print((char)data);
            }
            System.out.println();
            fileReader.close();
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
 
cs


Result)

1
2
Hi, I'm Sung-han LIM
Hi, I'm Sung-han LIM
cs



PipedReader와 PipedWriter

PipedReader와 PipedWriter는 쓰레드 간에 데이터를 주고받을 때 사용된다. 

다른 스트림과 달리 입력과 출력스트림을 하나의 스트림으로 연결(connect)해서 데이터를 주고받는다.

스트림을 생성한 다음에 어느 한쪽 쓰레드에서 connect()를 호출해서 입력스트림과 출력스트림을 연결한다. 

입출력을 마친 후에는 어느 한쪽 스트림만 닫아도 나머지 스트림은 자동으로 닫힌다. 이 점을 제외하고는 일반 입출력방법과 다르지 않다.


두 쓰레드가 PipedReader와 PipedWriter를 이용해서 서로 메시지를 주고받는 예제이다. 

Source01) PipedReaderWriter.java

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package inputOutput;
 
import java.io.IOException;
import java.io.PipedReader;
import java.io.PipedWriter;
import java.io.StringWriter;
 
public class PipedReaderWriter {
 
    public static void main(String[] args) {
        InputThread inThread = new InputThread("InputThread");
        OutputThread outThread = new OutputThread("OutputThread");
 
        /* PipedReader와 PipedWriter을 연결한다.
        쓰레드를 시작하기 전에 PipedReader와 PipedWriter를 연결해야한다. */
        inThread.connect(outThread.getOutput());
 
        inThread.start();
        outThread.start();
    }
}
 
class InputThread extends Thread {
    PipedReader input = new PipedReader();
    
 /* StringWriter는 메모리를 사용하는 스트림인데 내부적으로 StringBuffer를 가지고 있어서 
    출력하는 내용이 여기에 저장된다. */
    StringWriter sw = new StringWriter();
 
    public InputThread(String name) {
        super(name); // Thread(String name);
    }
 
    public void run() {
        try {
            int data = 0;
 
            while ((data = input.read()) != -1) {
                sw.write(data);
            }
            System.out.println(getName() + " received : " + sw.toString());
        } catch (IOException e) {
        }
    }
 
/*
    public PipedReader getInput() {
        return input;
    }
*/
 
    public void connect(PipedWriter output) {
        try {
            input.connect(output);
        } catch (IOException e) {
        }
    }
}
 
class OutputThread extends Thread {
    PipedWriter output = new PipedWriter();
 
    public OutputThread(String name) {
        super(name);// Thread(String name);
    }
 
    public void run() {
        try {
            String msg = "Hello";
            System.out.println(getName() + " sent : " + msg);
            output.write(msg);
            output.close();
        } catch (Exception e) {
        }
    }
 
    public PipedWriter getOutput() {
        return output;
    }
 
    public void connect(PipedReader input) {
        try {
            output.connect(input);
        } catch (IOException e) {
        }
    }
}
cs


Result)

1
2
OutputThread sent : Hello
InputThread received : Hello
cs


쓰레드를 시작하기 전에 PipedReader와 PipedWriter를 연결해야한다는 것에 유의하자.



StringReader와 StringWriter

StringWriter에 출력되는 데이터는 내부의 StringBuffer에 저장되며 

StringWriter의 다음과 같은 메서드를 이용해서 저장된 데이터를 얻을 수 있다.


StringBuffer getBuffer() : StringWriter에 출력한 데이터가 저장된 StringBuffer를 반환한다.

String toString() : StringWriter에 출력된 (StringBuffer에 저장된) 문자열을 반환한다.


Source01) StringReaderWriterEx.java
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
package inputOutput;
 
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
 
public class StringReaderWriterEx {
 
    public static void main(String[] args) {
        String inputData = "ABCD";
        StringReader input = new StringReader(inputData);
        
        /* StringWriter에 출력되는 데이터는 내부의 StringBuffer에 저장되며
        toString을 통해 저장된 데이터를 얻을 수 있다.*/
        StringWriter output = new StringWriter();
        
        int data = 0;
        
        try {
            while ((data = input.read())!=-1) {
                output.write(data);
            }
        } catch (IOException e) {}
        
        System.out.println("Input Data : "+ inputData);
        System.out.println("Output Data : "+ output.toString());
    }
}
cs

Result)
1
2
Input Data : ABCD
Output Data : ABCD
cs



출처 및 참고자료 : JAVA의정석(남궁성 저)


반응형

공유

댓글