Dlise
시원한 냉장고
Dlise
전체 방문자
오늘
어제
  • 시원한 냉장고 (136)
    • Java (31)
      • Java (26)
      • Spring (5)
    • Algorithm & PS (25)
      • Algorithm (14)
      • Problem Solving (11)
    • Network (12)
    • Database (2)
    • Data Structure (4)
    • OOP & CleanCode (5)
    • Web (0)
    • Git (2)
    • AI (2)
    • Project (1)
      • Discord Bot (1)
    • Error (19)
    • Tools (5)
    • 수학 (5)
      • 확률과 통계(기초) (5)
    • 컴퓨터 구조 (3)
    • 활동 (20)
      • 행사 & 여행 (10)
      • 자격증 (4)
      • 회고 (6)
      • 기타 (0)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 가장쉬운알고리즘책
  • 중위 표기법
  • CleanCode
  • 열혈강의자료구조
  • 백준
  • spring security in action second edition
  • 통계학
  • 네트워크
  • java
  • 후위 표기법

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Dlise

시원한 냉장고

Java/Java

Java - 바이트 기반 스트림(InputStream, OutputStream)

2023. 9. 13. 00:31

SWEA에서 문제를 풀면 사용 언어, 메모리 사용량, 실행시간, 코드 길이를 보여준다.

 

다른 사람들의 코드 및 결과도 확인이 가능한데

내가 푼 코드와 다른 사람들이 푼 코드의 메모리 사용량, 실행 시간의 차이가 큰 것을 발견했다.

 

이유가 궁금해 코드를 비교해 가며 하나하나 바꿔본 결과 I/O 과정에서 차이가 발생한 것이었다.

(내가 푼 코드: java.util.Scanner활용, 다른 사람들 코드: java.io.* 활용)

 

Java I/O를 바이트 기반 스트림, 문자 기반 스트림으로 구분해 학습하고자 한다.

https://dlise.tistory.com/81

 

Java - 문자 기반 스트림(Reader, Writer)

바이트 기반 스트림에 이어 문자 기반 스트림에 대해 알아보자. https://dlise.tistory.com/80 Java - 바이트 기반 스트림(InputStream, OutputStream) SWEA에서 문제를 풀면 사용 언어, 메모리 사용량, 실행시간, 코

dlise.tistory.com

 

 

스트림(Stream)

스트림(Stream)은 데이터를 운반하는 데 사용되는 연결 통로이다. 

스트림은 단방향 통신만 가능하기 때문에 양방향 통신을 하려면 2개의 스트림이 필요하다.

 

바이트 기반 스트림(InputStream, OutputStream)

InputStream과 OutputStream은 스트림을 바이트 단위로 전송하는 클래스이며,

아래의 클래스들은 각각 InputStream, OutputStream를 상속받아 대상에 맞게 변형된 것이다.

입력스트림 출력스트림 대상
FileInputStream FileOutputStream 파일
ByteArrayInputStream ByteArrayOutputStream 메모리
PipedInputStream PipedOutputStream 프로세스
AudioInputStream AudioOutputStream 오디오 장치

 

InputStream과 OutputStream에 정의되어 있는 메서드를 확인해 보자.

- InputStream

available() input stream으로부터 읽을 수 있는 바이트 크기를 반환한다.
close() stream을 닫고 자원을 반환한다.
mark(int readlimit) 현재 위치를 표시한다.
markSupported() mark()와 reset()을 지원하는지에 대해 true, false를 반환한다.
read() input stream의 다음 바이트를 반환한다. 반환할 값이 없으면 -1
read(byte[] b) 배열 b만큼 읽어서 배열을 채우고 읽은 바이트의 수를 반환한다.
read(byte[] b, int off, int len) 최대 len개의 byte를 읽어서 배열 b의 지정된 위치(off)부터 저장한다.
reset() mark로 표시한 위치로 되돌아간다.
skip(long n) stream에서 n만큼 건너뛴다.

 

-  OutputStream

close() stream을 닫고 자원을 반환한다.
flush() stream의 버퍼에 있는 모든 내용을 출력소스에 작성한다.
write(byte[] b) 배열 b에 있는 내용을 출력소스에 작성한다.
write(byte[] b, int off, int len) 배열 b에서 지정된 위치(off)부터 len만큼 출력소스에 작성한다.
write(int b) 주어진 값을 출력소스에 작성한다.

이제 상속받은 클래스들을 알아보면서 메서드를 사용해 보자.

 

ByteArrayInputStream, ByteArrayOutputStream

ByteArray는 바이트 배열에 데이터를 입출력할 때 사용된다.

 

아래 코드는 ByteArray I/O Stream를 이용해서 inputArr의 값을 outputArr로 옮기는 작업을 한다.

import java.util.Arrays;

public class IO {
    public static void main(String[] args) {
        byte[] inputArr = {1, 2, 3, 4, 5};
        byte[] outputArr = new byte[5];

        ByteArrayInputStream is = new ByteArrayInputStream(inputArr);
        ByteArrayOutputStream os = new ByteArrayOutputStream();

        int n;
        while((n = is.read()) != -1) {
            os.write(n);
        }

        outputArr = os.toByteArray();
        System.out.println(Arrays.toString(outputArr));
    }
}

read() 메서드로 -1을 반환할 때까지 while문을 돌려 write() 메서드를 불렀다.

이후 os에 저장된 byte 데이터들을 outputArr에 담았다.

 

read()와 write()의 활용 방법이 그다지 어렵지 않다는 것을 알 수 있다.

 

FileInputStream, FileOutputStream

File에 입출력한다.

 

아래 코드는 testFile.txt에서 한 글자씩 읽어서 터미널에 출력하는 코드와 결과이다.

testFile.txt에는 "Test File"이라는 문자열을 저장해 놓았다.

import java.io.*;

public class IO {
    public static void main(String[] args) {

        File file = new File("./practice/testFile.txt");

        try {
            FileInputStream is = new FileInputStream(file);
            int data = 0;
            while((data = is.read()) != -1)
                System.out.print((char)data);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

올바르게 출력된 것을 확인할 수 있다. 

한 문자씩 읽기 때문에 print대신 println을 사용하면 아래와 같이 출력된다.

 

 

 

바이트 기반 보조 스트림(FilterInputStream, FilterOutputStream)

간단히 위의 스트림들만 사용하기엔 비효율적이거나 불편한 부분이 있어서 이를 보조하는 스트림을 활용한다.

 

모든 바이트 기반 보조 스트림은 FilterInputStream, FilterOutputStream의 조상이다.

보조 스트림은 혼자선 I/O작업을 할 수 없고 기반 스트림을 활용해야 하는데, 위 두 클래스는 이러한 기반 스트림을 호출하는 역할을 한다.

다른 보조 스트림들은 Filter I/O Stream을 상속받아 기반 스트림을 호출하고 각 역할에 맞는 메서드를 추가로 가지고 있다.

 

관계는 아래와 같다.

 

BufferedInputStream, BufferedOutputStream

Buffered I/O Stream은 버퍼를 활용하는 보조 스트림이다.

버퍼를 활용하면 입출력이 확실히 빨라지기 때문에 사용하면 좋은 클래스이다.

 

아래 코드는 testFile.txt의 글을 읽어 버퍼에 담았다가 testFile2.txt에 출력하는 동작을 한다.

TestFile.txt에는 문자열 "Test File"이 들어있다.

import java.io.*;

public class IO {
    public static void main(String[] args) {
        
        try {
            File file = new File("./practice/testFile.txt");
            File file2 = new File("./practice/testFile2.txt");
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file2), 5);

            int data = 0;
            while((data = bis.read()) != -1)
                bos.write(data);
                
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

아래는 testFile2.txt이다. "Test File"이 아니라 "Test "가 적힌 모습이다.

그 이유는 BufferedOutputStream bos의 size가 5이기 때문으로 버퍼에 길이 5만큼 쌓일 때까지 출력하지 않는다.

 

이 문제는 bos를 close()하면 해결된다.

import java.io.*;

public class IO {
    public static void main(String[] args) {

        
        try {
            File file = new File("./practice/testFile.txt");
            File file2 = new File("./practice/testFile2.txt");
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file2), 5);

            int data = 0;
            while((data = bis.read()) != -1)
                bos.write(data);

            bis.close();
            bos.close();
            
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

bis, bos만 close()하면 기반 스트림도 close()된다.

아래는 보조 스트림의 close() 메서드로 if문을 통해 기반 스트림의 close()를 실행하는 것을 볼 수 있다.

 

보조스트림은 이외에도

DataInputStream, DataOutputStream: 데이터를 byte단위가 아닌 8가지 기본 자료형으로 봄

SequenceInputStream: 여러 개의 입력스트림을 하나의 스트림처럼 읽는 것 같이 처리

PrintStream: 데이터를 지정한 문자로 출력

등이 있다.

이들은 활용할 일이 없을 것 같아 더 찾아보진 않았다. 활용하게 되면 글을 따로 작성할 계획이다.

 

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

Java - StringTokenizer  (0) 2023.09.15
Java - 문자 기반 스트림(Reader, Writer)  (1) 2023.09.14
Java - Gradle Project 생성  (0) 2023.09.11
Java - 정규 표현식(Pattern, Matcher)  (0) 2023.09.10
Java - 정규 표현식(Regex)  (0) 2023.09.05
    'Java/Java' 카테고리의 다른 글
    • Java - StringTokenizer
    • Java - 문자 기반 스트림(Reader, Writer)
    • Java - Gradle Project 생성
    • Java - 정규 표현식(Pattern, Matcher)
    Dlise
    Dlise

    티스토리툴바