Language/Java

16-1. 입출력 I/O: 바이트기반 스트림

리져니 2021. 8. 8. 23:21

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

 

스트림(Stream)

자바에서 입출력을 수행하려면 두 대상을 연결하고 데이터를 전송 할 수 있는 스트림이 필요하다.

* 스트림: 데이터를 운반하는데 사용되는 연결통로

스트림은 단방향 통신만 가능함으로 하나의 스트림으로 입력과 출력을 동시에 처리할 수 없고, 만약 입력과 출력을 동시에 수행하고자 한다면 입력 스트림과 출력 스트림이 필요하다.

스트림은 먼저 보낸 데이터를 먼저 받게 되어 있으며 중간에  건너뜀 없이 연속적으로 데이터를 주고 받는다.

 

 

바이트기반 스트림: InputStream, OutputStream

스트림은 바이트 단위로 데이터를 전송하며 입출력 대상에 따라 입출력스트림이 있다.

* 입출력의 단위가 1byte

입력스트림 출력스트림 입출력 대상의 종류
FileInputStream FileOutputStream 파일
ByteArrayInputStream ByteArrayOutputStream 메모리 (byte배열)
PipedInputStream PipedOutputStream 프로세스 (프로세스간의 통신)
AudioInputStream AudioOuputStream 오디오장치

 

InputStream과 OutputStream의 메서드

InputStream
메서드 설명
int available() 스트림으로부터 읽어 올 수 있는 데이터의 크기를 반환
void close() 스트림을 닫음으로써 사용하고 있던 자원을 반환
void mark(int readlimit) 현재위치를 표시해 놓는다. 후에 reset()에 의해 표시해 놓은 위치로 다시 돌아갈 수 있다.
* readlimit: 되돌아갈 수 있는 byte의 수
boolean markSupported() mark()와 reset()을 지원하는 지를 알려준다. mark()와 reset() 기능을 지원하는 것은 선택정이므로, mark()와 reset()을 사용하기 전에 markSuppoerted()를 호출해서 지원여부를 확인해야함.
abstract int read() 1byte를 읽어온다. 더이상 읽어올 데이터가 없으면 -1을 반환.
int read(byte[] b) 배열 b의 크기만큼 읽어서 배열을 채우고, 읽어온 데이터의 수를 반환.
반환하는 값은 항상 배열의 크기보다 작거나 같다.
int read(byte[] b, int off, int len) 최대 len개의 byte를 읽어서. 배열 b의 지정된 위치(off)부터 저장.
실제로 읽어 올수 있는 데이터가 len개보다 적을수 있다.
void reset() 스트림에서의 위치를 마지막으로 mark()이 호출되었던 위치로 되돌린다.
long skip(long n) 스트림에서 주어진 길이(n)만큼 건너뛴다.

* 스트림의 종류에 따라서 mark()와 reset()을 사용하여 이미 읽은 데이터를 다시 읽을수 있다. (markSupported()를 통해 해당 기능을 지원하는 스트림인지 확인할수 있다.)

 

OutputStream
메서드 설명
void close() 입력소스를 닫음으로써 사용하고 있던 자원을 반환
void flush() 스트림의 버퍼에 있는 모든 내용을 출력소스에 씀
abstract void write(int b) 주어진 값을 출력소스에 씀
void write(byte[] b) 주어진 배열 b에 저장도니 모든 내용을 출력소스에 씀
void write(byte[] b, int off, int len) 주어진 배열 b에 저장된 내용 중에서 off번째부터 len개 만큼만 읽어서 출력소스에 씀

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

스트림을 사용한 작업을 마치고 나서 모두 close()를 호출해서 닫아주어야 하지만, ByteArrayInputStream과 같이 메머리를 사용하는 스트림과 Stream.in, System.out과 같은 표준 입출력 스트림은 닫아주지 않아도 된다.

 

ByteArrayInputStream과 ByteArrayOutputStream

메모리(바이트 배열)에 데이터를 입출력 하는데 사용되는 스트림. 주로 다른 곳에 입출력하기 전에 데이터를 임시로 바이트배열에 담아서 변환 등의 작업을 하는데 사용됨.

* 자주 사용되지는 않는다.

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;


public class Main {
    public static void main(String[] args) throws IOException {

        byte[] inSrc = {0,1,2,3,4,5,6,7,8,9};
        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);	// 읽어 온 데이터의 개수 반환
                output.write(temp, 0, len);	// 읽어 온 만큼만 write
            }
        }catch (IOException e){}

        outSrc = output.toByteArray();

        System.out.println("input source: " + Arrays.toString(inSrc));
        System.out.println("tmp: " + Arrays.toString(temp));
        System.out.println("output source: " + Arrays.toString(outSrc));
        

    }
}

실행결과

 

FileInputStream과 FileOutputStream

파일에 입출력을 하기 위한 스트림. 실제로 많이 쓰이는 스트림 중에 하나이다.

생성자 설명
FileInputStream(String name) 지정된 파일이름(name)을 가진 실제 파일과 연결된 FileInputStream을 생성
FileInputStream(File file) FileInputStream(String name)과 동일
* 파일의 이름이 String이 아닌 File인스턴스로 지정해주어야 한다.
FileInputStream(FileDescriptor fdObj) 파일 디스크립터(fdObj)로 FileInputStream을 생성
FileOutputStream(String name) 지정된 파일이름(name)을 가진 실제 파일과 연결된 File OutputStream을 생성
FileOutputStream(String name, boolean append) 지정된 파일이름(name)을 가진 실제 파일과 연결된 File OutputStream을 생성.
append를 true로 하면 출력시 기존의 파일내용의 마지막에 덧붙인다.
flase면 기존 파일내용을 덮어쓰게 된다.
FileOutputStream(File file) FileOutputStream(String name)과 동일
* 파일의 이름을 String이 아닌 File인스턴스로 지정해주어야한다.
FileOutputStream(File file, boolean append) FileOutputStream(String name, boolean append)
* 파일의 이름을 String이 아닌 File인스턴스로 지정해주어야한다.
FileOutputStream(FileDescriptor fdObj) 파일 디스크립터(fdObj)로 FileOutputStream을 생성

 

보조 스트림

스트림의 기능을 보완하기 위한 스트림. 실제 데이터를 주고받는 스트림은 아니지만, 스트림의 기능을 향상시키거나 새로운 기능을 추가할 수 있다.

* 스트림을 먼저 생성한 다음에 이를 이용해서 보조스트림을 생성해야함

FileInputStream fis = new FileInputStream("test.txt");	// 기반 스트림 생성
BufferedInputStream bis = new BufferedInputStream(fis);	// 기반 스트림을 이용해서 보조 스트림 생성
bis.read();	// 보조스트림으로부터 데이터를 읽음

실제 입력기능은 FileInputStream이 수행하고, 보조스트림인 BufferedInpuStream은 버퍼만 제공한다.

 

입력 출력 설명
FilterInputStream FilterOutputStream 필터를 이용한 입출력 처리
BufferedInputStream BufferedOutputStream 버퍼를 이용한 입출력 성능향상
DataInputStream DataOutPutStream int, float와 같은 기본형 단위로 데이터를 처리
SequenceInputStream   두 개의 스트림을 하나로 연결
LineNumberInputStream   읽어온 데이터의 라인 번호를 카운트
ObjectInputStream ObjectOutputStream 데이터를 객체단위로 읽고 쓰는데 사용.
주로 파일을 이용하여 객체 직렬화와 관련
  PrintStream 버퍼를 이용하며, 추가적인 print관련 기능
PushbackInputStream   버퍼를 이용해서 읽어 온 데이터를 다시 되돌림

 

 

바이트기반의 보조스트림: FileterInputStream과 FilterOutputStream

InputStream과 OutputStream의 자손이면서 모든 보조스트림의 조상이다. 

 

FileterInputStream과 FilterOutputStream의 모든 메서드는 단순히 기반스트림의 메서드를 그대로 호출한다.

보조스트림 자체로는 아무일도 하지 않음. 상속을 통해 원하는 작업을 수행하도록 읽고 쓰는 메서드를 오버라이딩 해야함(FileterInputStream의 생성자의 접근 제어자가 protect이기 때문).

 

FileterInputStream과 FilterOutputStream를 상속받아 기반스트림에 보조 기능을 추가한 보조스트림 클래스 종류

 

BufferedInputStream과 BufferedOutputStream

스트림의 입출력 효율울 높이기 위해 버퍼를 사용하는 보조스트림.

한 바이트씩 입출력하는 것 보다는 버퍼를 이용해서 한번에 여러 바이트를 입출력하는 것이 빠르다.

BufferedInputStream의 생성자
BufferedInputStream(InputStream in, int size) 주어진 InputStream인스턴스를 입력소스로 하며 지정된 크기의 버퍼를 갖는 BufferedInputStream인스턴스를 생성
BufferedInputStream(InputStream in) 주어진 InputStream인스턴스를 입력소스로 하며, 기본적으로 8192byte크기의 버퍼를 갖는 BufferedInputStream인스턴스를 생성

* BufferedInputStream의 버퍼 크기는 입력소스로부터 한번에 가져올수 있는 데이터 크기로 지정하면 좋다.

입력 소스로부터 데이터를 읽기위해 read메서드를 호출하면 BufferedInputStream은 입력 소스로부터 버퍼 크기만큼의 데이터를 읽어서 자신의 내부 버퍼에 저장하고 프로그램에서 BufferedInputStream의 버퍼에 저장된 데이터를 읽게된다.

외부의 입력소스로부터 읽는 것보다 내부의 버퍼로부터 읽는 것이 훨씬 빠르고 작업 효율이 높다.

 

BufferedOutputStream의 생성자와 메서드
BufferedOutputStream(OutputStream out, int size) 주어진 OutputStream인스턴스를 출력소스로하며 지정된 크기의 버퍼를 갖는 BufferedOutputStream인스턴스를 생성
BufferedOutputStream(OutputStream out) 주어진 OutputStream인스턴스를 출력소스로하며 8192byte크기의 버퍼를 갖는 BufferedOutputStream인스턴스를 생성
flush() 버퍼의 모든 내용을 출력소스에 출력한 다음, 버퍼를 비움
close() flush()를 호출해서 버퍼의 모든 내용을 출력소스에 출력하고, BufferedOutputStream인스턴스가 사용하던 모든 자원 반환

입력소스로부터 데이터를 읽을 때와는 반대로,  프로그램에서 write메서드를 이용한 출력이 BufferedOutputStream의 버퍼에 저장된다. 버퍼가 가득차면, 버퍼의 모든 내용을 출력소스에 출력하고 버퍼를 비운뒤 다시 프로그램으로부터의 출력을 저장할 준비를 한다.

* 버퍼가 가득 찼을때만 출력소스에 출력을 하기 때문에, 마지막 출력부분이 출력소스에 쓰이지 못하고 버퍼에 남아있는 채로 프로그램이 종료될수 있다. 때문에 close()나 flush()를 호툴해서 마지막 버퍼에 있는 모든 내용이 출력되도록 해야한다.

 

예제- 크기가 5인 BufferedOutputStream을 이용해서 123.txt에 1부터 9까지의 숫자 입력하기

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;


public class Main {
    public static void main(String[] args) throws IOException {
        try {
            FileOutputStream fos = new FileOutputStream("123.txt");
            BufferedOutputStream bos = new BufferedOutputStream(fos, 5);
            for (int i = '1'; i <= '9'; i++) {
                bos.write(i);
            }

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

실행 결과

9까지 입력하려고 했지만 5까지만 출력되었다. 그 이유는 버퍼에 남아있는 데이터가 출력되지 못한 상태로 프로그램이 종료되었기 때문이다.

 

예제 수정

bos.close()를 추가하여 버퍼에 남아있는 모든 내용이 출력되도록 한다.

 

실행결과

 

DataInputSream과 DataOutputStream

FileterInputStream과 FilterOutputStream의 자손이며, DataInputStream은 DataInput인터페이스를, DataOutputStream은 DataOutput인터페이스를 각각 구현하여 데이터를 읽고 쓰는데 Byte단위가 아닌 8가지 기본 자료현의 단위로 읽고 쓸수 있다.

* DataOutputStream이 출력하는 형식은 각 기본 자료형 값을 16진수로 표현하여 저장한다.

각 자료현의 크기가 다르기 때문에 출력한 데이터를 다시 읽어올 때는 출력했을 때의 순서를 염두해야 한다.

DataInputSream
DataInputStream(IntputStream in) 주어진 InputStream인스턴스를 기반스트림으로 하는 DataInputStream인스턴스를 생성
boolean    readBoolean()
byte         readByte()
char         readChar()
short        readShort()
int           readInt()
long        readLong()
float        readFloat()
double     readDouble()
int          readUnsignedByte()
int          readUnsignedShort()
각 타입에 맞게 값을 읽어온다.
더이상 읽을 값이 없으면 EOFException을 발생시킴
void readFully(byte[] b)
void readFully(byte[] b, int off, int len)
입력스트림에서 지정된 배열의 크기만큼 또는 지정된 위치에서 len만큼 데이터를 읽어온다.
파일의 끝에 도달하면 EOFException이 발생하고, I/O에러가 발생하면 IOException이 발생한다
String readUTF() UTF-8형식으로 쓰여진 문자를 읽는다.
더이상 읽을 값이 없으면 EOFException이 발생
static String readUTF(DataInput in) 입력스트림(in)에서 UTF-8형식의 유니코드를 읽어옴
int skipBytes(int n) 현재 읽고 있는 위치에서 지정된 숫자(n)만큼을 건너뜀

 

DataOutputStream
DataOutputStream(OutputStream out) 주어진 OutputStream인스턴스를 기반스트림으로 하는 DataOutputStream인스턴스를 생성
void writeBoolean(boolean b)
void writeByte(int b)
void writeChar(int v)
void writeChars(String s)
void writeShort(int s)
void writeInt(int l)
void writeLong(long l)
void writeFloat(floar l)
void writeDouble(double d)
각 자료형에 맞는 값들을 출력
void writeUTF(String s) UTF형식으로 문자 출력
void writeChars(String s) 주어진 문자열을 출력. writeChar(int c)메서드를 여러번 호출한 결과와 같다
int size() 지금까지 DataOutputStream에 쓰여진 byte의 수를 알려줌

 

SequenceInputStream

여러 개의 입력스트림을 연속적으로 연결해서 하나의 스트림으로부터 데이터를 읽는 것과같이 처리할 수 있도록 도와준다. SequenceInputStream의 생성자를 제외한 나머지 작업은 다른 입력스트림과 비슷하다.

* 큰 파일을 여러 개의 작은 파일로 나누었다가, 하나의 파일로 합치는 것과 같은 작업을 수행할 때 사용하면 좋다

SequenceInputStream
SequenceInputStream(Enumeration e) Enumeration에 저장된 순서대로 입력스트림을 하나의 스트림으로 연결한다.
SequenceInputStream(InputStream s1, InputStream s2) 두 개의 입력스트림을 하나로 연결한다.

 

PrintStream

데이터를 기반스트림에 다양한 형태로 출력할 수 있는 print, println, printf와 같은 메서드를 오버로딩하여 제공한다.

데이터를 적절한 문자로 출력하는 것이기 때문에 문자기반 스트림의 역할을 수행한다.

* PrintStream과 PrintWriter는 거의 같은 기능을 가지고 있지만, PrintWriter가 PrintStream에 비해 다양한 언어의 문자를 처리하는데 적합하기 때문에 가능하면 PrintWriter를 사용하는 것이 좋다.

 

printf()사용시 출력에 사용될수 있는 옵션

format 설명 결과
%d 10진수 65
%o 8진수 101
%x 16진수 41
%c 문자 A
%s 문자열 65
%5d 5자리 숫자. 빈자리는 공백으로 채운다 (오른쪽 정렬)    65
%-5d 5자리 숫자. 빈자리는 공백으로 채운다 (왼쪽 정렬) 65
%05d 5자리 숫자. 빈자리는 0으로 채운다 00065
%s 문자열 ABC
%5s 5자리 문자열. 빈자리는 공백으로 채운다 (오른쪽 정렬)   ABC
%-5s 5자리 문자열. 빈자리는 공백으로 채운다 (왼쪽 정렬) ABC  
%e 지수형태표현 1.234568e+03
%f 10진수 1234.56789
%3.1f 출력될 자리수를 최소 3자리(소수점포함), 소수점 이하 1자리 (2번째 자리에서 반올림) 1234.6
%8.1f 소수점이상 최소 6자리, 소수점 이하 1자리.
출력될 자리를 최소 8자리(소수점포함)를 확보한다.
빈자리는 공백으로 채워진다 (오른쪽 정렬)
  1234.6
%08.1f 소수점이상 최소 6자리, 소수점 이하 1자리.
출력될 자리를 최소 8자리(소수점포함)를 확보한다.
빈자리는 0으로 채워진다 (오른쪽 정렬)
001234.6
%-8.1f 소수점이상 최소 6자리, 소수점 이하 1자리.
출력될 자리를 최소 8자리(소수점포함)를 확보한다.
빈자리는 공백으로 채워진다 (왼쪽 정렬)
1234.6
\t 탭(tab)  
%n 줄바꿈 문자  
%% %  
%tR
%tH:%tM
시분(24시간) 21:05
     
     
     
     
     

 

728x90

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

[JAVA] 정규 표현식  (0) 2021.10.28
15-4(2). 스트림의 최종 연산 - Collect  (0) 2021.08.05
15-4(1). 스트림의 최종 연산  (0) 2021.08.05
15-3. Optional  (0) 2021.08.05
15-2. 스트림의 중간 연산  (0) 2021.08.05