[Java] 바이트 기반의 스트림


바이트 기반의 스트림


1. 바이트 기반 스트림

(1) InputStream과 OutputStream

InputStream과 OutputStream은 모든 바이트기반의 스트림의 조상이며 같은 메서드가 선언되어 있다.

cp.)

void close(): 스트림을 닫음으로써 사용하고 있던 자원을 반환한다.

abstract int read(): 1byte를 읽어온다.(0~255 사이의 값), 더 이상 읽어 올 데이터가 없으면 -1을 반환한다. abstract메서드라서 InputStream의 자손들은 자신의 상황에 알맞게 구현해야 한다.

int read(byte[] b): 배열 b의 크기만큼 읽어서 배열을 채우고 읽어 온 데이터의 수를 반환한다. 반환하는 값은 항상 배열의 크기보다 작거나 같다.

int read(byte[] b, int off, int len): 최대 len개의 byte를 읽어서, 배열 b의 지정된 위치(off)부터 저장한다. 실제로 읽어올 수 있는 데이터가 len개보다 적을 수 있다.

flush() 버퍼가 있는 출력 스트림의 경우에만 의미가 있으며, OutputSteam에 정의된 flush()는 아무런 일도 하지 않는다.

프로그램이 종료될 때, 사용하고 닫지 않은 스트림을 JVM이 자동적으로 닫아 주기는 하지만, 스트림을 사용해서 모든 작업을 마치고 난 후에는 close()를 호출해서 반드시 닫아 주어야 한다.

cf.) ByteArrayInputStream과 같이 메모리를 사용하는 스트림과 System.in, System.out과 같은 표준입출력은 닫아 주지 않아도 된다.


(2) FileInputStream과 FileOutputStream
FileInputStream과 FileOutputStreamdms 파일에 입출력을 하기 위한 스트림이다.


cp.) FileInputStream(File fileObj): 파일의 이름이 String이 아닌 File 인스턴스로 지정해주어야 하는 점을 제외하고 FileInputStream(String filePath)와 같다.


import java.io.*;


class FileViewer {
public static void main(String args[]) throws IOException{
FileInputStream fis = new FileInputStream(args[0]);
int data =0;
while((data=fis.read())!=-1) {
char c = (char)data;
System.out.print(c);
}
}
}

FileInputStream과 FileOutputStream을 사용해서 FileCopy.java파일의 내용을 그대로 복사하는 내용의 예제이다.
import java.io.*;

class FileCopy {
public static void main(String args[]) {
try {
FileInputStream fis = new FileInputStream(args[0]);
FileOutputStream fos = new FileOutputStream(args[1]);

int data =0;

while((data=fis.read())!=-1) {
fos.write(data); // void write(int b)
}

fis.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}


import java.io.*;

class FileViewer {
public static void main(String args[]) throws IOException{
FileInputStream fis = new FileInputStream(args[0]);
int data =0;
while((data=fis.read())!=-1) {
char c = (char)data;
System.out.print(c);
}
}
}
read()의 반환값이 int형(4byte)이긴 하지만, 더 이상 입력값이 없음을 알리는 -1을 제외하고는 0~255(1byte)의 범위의 정수값이기 때문에, char형(2byte)으로 변환한다해도 손실되는 값이 없다.
read()가 한 번에 1byte씩 파일로부터 데이터를 읽어 들이긴 하지만, 데이터의 범위가 십진수로 0~255(16진수로는 0x00~0xff)범위의 정수값이고, 또 읽을 수 있는 입력값이 더 이상 없음을 알릴 수 있는 값도 필요하다.
그래서 다소 크긴 하지만 정수형 중에서는 연산이 가장 효율적이고 빠른 int형 값을 반환하도록 한 것이다.

2. 바이트기반의 보조스트림
(1) FilterInputStream과 FilterOutputStream

FilterInputStream과 FilterOutputStream은 InputStream/OutputStream의 자손이면서 모든 보조 스트림의 조상이다. 
보조 스트림은 자체적으로 입출력을 수행할 수 없기 때문에 기반 스트림을 필요로 한다.

- FilterInputStream과 FilterOutputStream의 생성자이다.
Protected FilterInputStream(InputStream in)
public FilterOutputStream(OutputStream out)
FilterInputStream과 FilterOutputStream의 모든 메서드는 단순히 기반 스트림의 메서드를 그대로 호출할 뿐이다. FilterInputStream과 FilterOutputStream 자체로는 아무런 일도 하지 않음을 의미한다.
FilterInputStream과 FilterOutputStream 자체로는 아무런 일도 하지않고 상속을  통해 원하는 작업을 수행하도록 읽고 쓰는 메서드를 오버라이딩 해야 한다.

(2) BufferedInputStream과 BufferedOutputStream
BufferedInputStream과 BufferedOutputStream은 스트림의 입출력 효율을 높이기 위해 버퍼를 사용하는 보조 스트림이다. 한 바이트씩 입출력하는 것 보다 버퍼(바이트배열)를 사용해서 한 번에 여러 바이트를 입출력하는 것이 빠르기 때문에 대부분의 입출력 작업에 사용된다.

BufferedInputStream과 BufferedOutputStream의 버퍼크기는 입력소스로부터 한 번에 가져올 수 있는 데이터의 크기로 지정하면 좋다. 보통 입력소스가 파일인 경우 보통 작게는 1024부터 2048 또는 4096 정도의 크기로 하는 것이 보통이며, 버퍼의 크기를 변경해가면서 테스트하면 최적의 버퍼 크기를 알아낼 수 있다.

프로그램에서 입력 소스로부터 데이터를 읽기 위해 처음으로 read 메서드를 호출하면, BufferedInputStream은 입력 소스로 부터 버퍼 크기만큼의 데이터를 읽어다 자신의 내부 버퍼에 저장한다. 이제 프로그램에서는 
BufferedInputStream의 버퍼에 저장된 데이터를 읽으면 되는 것이다. 외부의 입력 소스로 부터 읽는 것보다 내부의 버퍼로 읽는 것이 훨씬 빠르기 때문에 그만큼 작업 효율이 높아진다.
프로그램에서 버퍼에 저장된 모든 데이터를 다 읽고 그 다음 데이터를 읽기 위해 read 메서드가 호출되면, BufferedInputStream은 입력 소스로부터 다시 버퍼 크기만큼의 데이터를 읽어다 버퍼에 저장해 놓는다.


BufferedOutputStream 역시 버퍼를 이용해서 작업을 하게 되는데, 입력 소스로부터 데이터를 읽을 때와는 반대로, 프로그램에서 write 메서드를 이용한 출력이 BufferedOutputStream의 버퍼에 저장된다. 
버퍼가 가득 차면, 그 때 버퍼의 모든 내용을 출력 소스에 출력한다.
버퍼가 가득 찼을때만 출력소스에 출력을 하기 때문에, 마지막 출력 부분이 출력소스에 쓰여지지 못하고, BufferedOutputStream의 버퍼에 남아있는 채로 프로그램이 종료될 수 있다는 점을 주의해야 한다.
그래서 프로그램에서 모든 출력 작업을 마친 후 BufferedOutputStream에 close()나 flush()를 호출해서 마지막에 버퍼에 있는 모든 내용이 출력소스에 출력되도록 해야 한다.

import java.io.*;

class BufferedOutputStreamEx1 {
public static void main(String args[]) {
try {
    FileOutputStream fos = new FileOutputStream("123.txt");      
             BufferedOutputStream bos = new BufferedOutputStream(fos, 5);
     // BufferedOutputStream의 버퍼 크기를 5로 한다.
   
    for(int i='1'; i <= '9'; i++) {   
    bos.write(i);
     // 파일 123.txt에  1 부터 9까지 출력한다.
    }
    fos.close();
} catch (IOException e) {
    e.printStackTrace();
}
}
}
크기가 5인 BufferedOutputStream을 이용해서 파일 123.txt에 1부터 9까지 출력하는 예제인데 결과를 보면 5까지만 출력된 것을 알 수 있다. 그 이유는 버퍼에 남아있는 데이터가 출력되지 못한 상태로 프로그램이 종료되었기 때문이다.
이 예제에서 fos.close()를 호출해서 스트림을 닫아주기는 했지만, 이렇게 해서는 BufferedOutputStream의 버퍼에 있는 내용이 출력되지 않는다. bos.close();와 같이 해서 BufferedOutputStream의 close()를 호출해 주어야 버퍼에 남아있던 모든 내용이 출력된다. BufferedOutputStream의 close()는 기반 스트림인 FileOutputStream의 close()를 호출하기 때문에 FileOutputStream의 close()는 따로 호출해주지 않아도 된다.
--> 보조스크림을 사용한 경우에는 기반스트림의 close()나 flush()를 호출할 필요없이 단순히 보조스트림의 close()를 호출하기만 하면 된다.

(3) PrintStream
PrintStream은 데이터를 기반스트림에 다양한 형태로 출력할 수 있는 print, println, printf와 같은 메서드를 오버로딩하여 제공한다.
PrintStream은 데이터를 적절한 문자로 출력하는 것이기 때문에 문자기반 스트림의 역할을 수행한다.
cf.) PrintStream은 우라기 지금까지 알게 모르게 많이 사용해 왔다. System 클래스의 static 멤버인 out과 err, 다시 말하자면 System.out, System.err이 PrintStream이다.


import java.util.Date;

class PrintStreamEx1 {
public static void main(String[] args) {
int i = 65;
float f = 1234.56789f;

Date d = new Date();

System.out.printf("문자 %c의 코드는 %d\n", i, i);
System.out.printf("%d는 8진수로 %o, 16진수로 %x\n", i ,i, i);
System.out.printf("%3d%3d%3d\n", 100, 90, 80);
System.out.println();
System.out.printf("123456789012345678901234567890\n");
System.out.printf("%s%-5s%5s\n", "123", "123", "123");
System.out.println();
System.out.printf("%-8.1f%8.1f %e\n",f,f,f);
System.out.println();
System.out.printf("오늘은 %tY년 %tm월 %td일 입니다.\n", d,d,d,d );
System.out.printf("지금은 %tH시 %tM분 %tS초 입니다.\n", d,d,d,d );
System.out.printf("지금은 %1$tH시 %1$tM분 %1$tS초 입니다.\n", d );
}
}
실행결과)
문자 A의 코드는 65
65는 8진수로 101, 16진수로 41
100 90 80

123456789012345678901234567890
123123    123

1234.6    1234.6 1.234568e+03

오늘은 2014년 04월 23일 입니다.
지금은 01시 50분 29초 입니다.
지금은 01시 50분 29초 입니다.



'Programing > Java' 카테고리의 다른 글

[Java] File 클래스  (0) 2014.12.16
[Java] 문자 기반 스트림  (0) 2014.12.16
[Java] 파일I/O 개요  (0) 2014.12.16
[Java] 제네릭  (3) 2014.12.14
[Java] 컬렉션 프레임워크  (0) 2014.12.14