[JAVA] 입출력 스트림

DANI·2023년 10월 13일
0

JAVA를 공부해보자

목록 보기
13/29
post-thumbnail

📕 Stream(스트림)이란?

스트림은 쉽게 이야기해서 Byte 형태로 데이터를 운반하는데 사용되는 연결통로라고 생각하면 된다.

이는 자료(data)의 흐름이 물의 흐름과 같다는 의미에서 사용되었다고 한다. 다만 물이 한쪽 방향으로만 흐르는 것과 같이 스트림은 단방향 통신만 가능하기 때문에 하나의 스트림으로 입력과 출력을 동시에 처리 할 수 없다.

또한 스트림은 먼저 보낸 데이터를 먼저 받게 되어있으며 연속적으로 데이터를 주고 받는다는 점에서 큐(queue)의 FIFO(First in Frist Out) 구조로 되어 있다.

이때 데이터의 스트림의 근원지(시작점)을 Source, 데이터 종착점을 Sink, 연결한 것을 Stream 이라고 표현하며, Source - 입력 스트림 input Stream- 출력 스트림 output Stream- Sink 으로 연결된다.



❓ Stream(스트림)의 특징


  • 여러 개의 스트림을 연결하여 사용할 수 있다.

  • 파일 입출력 동안 예외 발생 가능

    1. 스트림 생성 동안FileNotFoundException 발생 가능!!
      ----> 파일의 경로명이 틀리거나 디스크의 고장 등으로 파일을 열 수 없을 때

    2. 파일 읽기, 쓰기, 닫기를 하는 동안: IOException 발생 가능!!
      ----> 디스크 오동작, 파일이 중간에 깨진 경우, 디스크 공간이 모자라서 파일 입출력이 불가능한 경우.

      즉, try-catch 블록이 반드시 필요하다.

  • 스트림을 이용하여 실제 다양한 하드웨어와 입출력을 수행하는 일은 JVM에 의해 실행된다.

  • 자바는 입출력을 위한 클래스들을 java.io 패키지로 제공하고있다.

  • 스트림은 단방향이다. (방향에 따라 입력 스트림, 출력 스트림)

  • 스트림은 선입선출, FIFO 구조이다.

  • 스트림은 지연될 수 있다.

    . 입력 스트림이 흐르는 통로인 파이프가 비어 있다면, 컴퓨터는 읽어갈 데이터가 없으므로 기다림

    . 출력 스트림이 흐르는 통로인 파이프에 데이터가 꽉 차 있다면 컴퓨터는 빈 공간이 생길 때까지 기다린다.




📝 1. 입출력 스트림의 분류

  • 바이트(Byte)기반 스트림
    1. 그림, 멀티미디어등의 바이너리 데이터를 읽고 출력할 때 사용
    2. 바이트(8비트)를 읽고 쓰기 위한 스트림
    3. 문자 데이터든 바이너리 데이터든 상관없이 처리 가능
  • 문자(Character)기반 스트림
    1. 문자 데이터를 읽고 출력할 때 사용
    2. 16비트 문자나 문자열을 읽고 쓰기 위한 스트림
    3. 문자가 아닌 바이너리 데이터는 스트림에서 처리하지 못함
    4. 문자가 아닌 데이터를 문자 스트림으로 출력하면 깨져서 출력된다.

📝 1-1. 바이트 출력 스트림 : OutputStream


💡 OutputStream의 주요 메소드

리턴 타입메소드설명
voidwrite(int b)1byte 출력
voidwrite(byte[] b)매개값으로 주어진 배열b의 모든 바이트 출력
voidwrite(byte[] b, int off, int len)매개값으로 주어진 배열b[off]부터 len개까지의 바이트 출력
voidflush()출력버퍼에 잔류하는 모든 바이트 출력
voidclose()출력스트림 닫기


🔴 write(int b) 추상 메소드

public abstract void write(int b) throws IOException;

🔴 1. Write(int b) 메소드 예시

import java.io.FileOutputStream;
import java.io.OutputStream;

public class WriteExample {
	public static void main(String[] args) throws Exception {
		OutputStream os = new FileOutputStream("C:/Temp/test1.db"); // 데이터 도착지를 .db로 함
		// 경로를 지정해주지 않으면 root에 만들어짐!

		byte a = 10;
		byte b = 20;
		byte c = 30;
		

		os.write(a);
		os.write(b);
		os.write(c);
		
		os.flush(); // 현재 파일 출력 스트림의 버퍼에 보관중인 데이터를 파일로 출력
		os.close(); // 모든 스트림 작업이 끝나면 공식적으로 닫아줘야함
					// (안 닫을 경우 메모리 누수 발생할 수 있음)
	}
}

입출력 스트림은 항상 예외처리를 해줘야 한다


🔵 실행 결과

"C:/Temp/" 경로에 test1.db 파일이 생성되었다!



🔴 Write(byte[] b) 메소드

public void write(byte b[]) throws IOException {
        write(b, 0, b.length);
    }

🔴 2. Write(byte[] b) 메소드 예시

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class WriteBytesExample {

	public static void main(String[] args) throws IOException {
		OutputStream os = new FileOutputStream("c:/TEMP/test2.db");
		
		byte[] bytes = {10, 20, 30};
		
		os.write(bytes);
		
		os.flush();
		os.close();	
	}
}

🔵 실행 결과




🔴 write(byte[] b, int off, int len) 메소드

public void write(byte buf[], int off, int len) {
        try {
            synchronized (this) {
                ensureOpen();
                out.write(buf, off, len);
                if (autoFlush)
                    out.flush();
            }
        }
        catch (InterruptedIOException x) {
            Thread.currentThread().interrupt();
        }
        catch (IOException x) {
            trouble = true;
        }
    }

🔴 3. write(byte[] b, int off, int len) 메소드 예시

import java.io.IOException;
import java.io.PrintStream;

public class PrintStreamExample {

	public static void main(String[] args) throws IOException {
		PrintStream os = new PrintStream("C:/Temp/test3.db");
		
		byte[] array = {10,20,30,40,50};
		
		os.write(array, 1, 3); // 배열의 1번부터 3개를 출력
		
		os.flush();
		
		os.close();

	}
}

🔵 실행 결과




📝 1-2. 바이트 입력 스트림 : InputStream


💡 InputStream의 주요 메소드

리턴 타입메소드설명
intread()1byte 읽고 읽은 바이트 리턴
intread(byte[] b)읽은 바이트를 매개값으로 주어진 배열에 저장하고 읽은 바이트 수를 리턴
intread(byte[] b, int off, int len)len개의 바이트를 읽고 매개값으로 주어진 배열에서 b[off]부터 len개까지 저장 후 읽은 바이트 수를 리턴
voidclose()입력스트림 닫기


🔴 read() 추상 메소드

 public abstract int read() throws IOException;

🔴 1. read() 메소드 예시

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class ReadExample {
	public static void main(String[] args) throws IOException {
		InputStream is = new FileInputStream("C:/TEMP/test1.db");
		
		while(true) {
			int data = is.read(); // read() 메소드는 리턴값이 int라서 int로 선언한다.
			
			if(data==-1) break; // 더 이상 입력 스트림으로부터 바이트를 읽을 수 없을 때
								// read() 메소드는 -1을 리턴함
			
			System.out.println(data);
		}
		
		is.close();
	}
}

"C:/TEMP/test1.db" 파일은 출력스트림 wirte()예제에서 만들었다!


🔵 실행 결과

10
20
30


🔴 read(byte[] b) 메소드

public int read(byte b[]) throws IOException {
        return read(b, 0, b.length);
    }

🔴 2. read(byte[] b) 메소드 예시

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class ReadExample_1 {

	public static void main(String[] args) throws IOException {
		
		InputStream is = new FileInputStream("C:/TEMP/test2.db"); // {10,20,30}이 저장되어 있음
		
		byte[] buffer = new byte[100]; // 크기가 100인 바이트 타입의 배열 생성
		
		while(true) {
			
			// is에 있는 데이터를 버퍼로 읽어 와서 data에 저장한다.
			int data = is.read(buffer);
			
			// 더 이상 읽어올 수 없을 때 data는 -1을 리턴한다.
			if(data==-1) break;
			
			// 버퍼에 저장된 배열을 읽어오기
			for(int i=0; i<data; i++) {
				System.out.println(buffer[i]);
			}
			// 버퍼에 저장된 배열의 길이
			System.out.println(data); 
		}
		is.close();
	}
}

🔵 실행 결과

10
20
30
3 // 버퍼에 저장된 배열의 길이


💡 Buffer(버퍼)란?


임시로 데이터를 담아둘 수있는 일종의 큐이다. 바이트 단위의 데이터가 입력될 때마다 Stream은 즉시 전송하게 되는데 이것은 디스크 접근이나 네트워크 접근같은 오버헤드가 발생하기 때문에 매우 비효율적인 방법이다. Buffer는 중간에서 입력을 모아서 한번에 출력함으로써 I/O 의 성능을 향상시키는 역할을 한다.



🔴 read(byte[] b, int off, int len) 메소드

public int read(byte b[], int off, int len) throws IOException {
        Objects.checkFromIndexSize(off, len, b.length);
        if (len == 0) {
            return 0;
        }

        int c = read();
        if (c == -1) {
            return -1;
        }
        b[off] = (byte)c;

        int i = 1;
        try {
            for (; i < len ; i++) {
                c = read();
                if (c == -1) {
                    break;
                }
                b[off + i] = (byte)c;
            }
        } catch (IOException ee) {
        }
        return i;
    }

🔴 3. read(byte[] b, int off, int len) 메소드 예시

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class ReadExample_2 {

	public static void main(String[] args) throws IOException {
		InputStream is = new FileInputStream("C:/Temp/test3.db");
		
		byte[] buffer = new byte[5];
		
		// test.db -> {20,30,40}
		// buffer[2], buffer[3], buffer[4]로 저장해라
		int len = is.read(buffer, 2, 3); 
		
		// 읽은 바이트가 있다면
		if(len != -1) {
			
			// 배열 전체를 출력
			for(int i=0; i<buffer.length; i++) {
				System.out.println(buffer[i]);
			}
		}
		is.close();
	}
}

🔵 실행 결과

0
0
20
30
40



📝 2-1. 문자 출력 스트림 : Writer


💡 Writer 클래스의 주요 메소드

리턴 타입메소드설명
voidwrite(int b)매개값으로 주어진 한 문자를 보냄
voidwrite(char[] cbuf)매개값으로 주어진 배열의 모든 문자 보냄
voidwrite(char[] cbuf, int off, int len)매개값으로 주어진 배열에서 char[off]부터 len개까지의 문자를 보냄
voidwrite(String str)매개값으로 주어진 문자열을 보냄
voidwrite(String str, int off, int len)매개값으로 주어진 문자열에서 off순번부터 len개까지의 문자를 보냄
voidflush()버퍼에 잔류하는 모든 문자 출력
voidclose()출력스트림 닫기

🔴 1. Write(int c) 메소드 예시

import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

public class WriteExample {

	public static void main(String[] args) throws IOException {
		Writer writer = new FileWriter("C:/TEMP/test7.txt");
		
		
		char a = 'A';
		char b = 'B';
		char c = 'C';
		
		// 한 문자씩 출력
		writer.write(a);
		writer.write(b);
		writer.write(c);
		
		writer.flush();
		writer.close();
	}
}

🔵 실행 결과



🔴 2. write(char[] cbuf) 메소드 예시

import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

public class WriteExample_1 {
	public static void main(String[] args) throws IOException {
		Writer writer = new FileWriter("C:/TEMP/test8.txt");
		
		char[] array = {'중', '앙', '인','재','개','발','원'};
		
        // 배열의 모든 문자열 출력
		writer.write(array); 
		
		writer.flush();
		writer.close();
	}
}

🔵 실행 결과

💡 유니코드를 다루는 이유? : 한글로 써도 깨지지 않음(UTF-8)




🔴 3. write(char[] cbuf, int off, int len) 메소드 예시

import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

public class WriteExample_2 {
	public static void main(String[] args) throws IOException {
		Writer writer = new FileWriter("C:/TEMP/test9.txt");
		
		char[] array = {'중', '앙', '인','재','개','발','원'};
		
		// 배열의 인덱스1번부터 3개를 출력해라
		writer.write(array, 1, 3);
		
		writer.flush();
		writer.close();
	}
}

🔵 실행 결과


🔴 4. write(String str), write(String str, int off, int len) 메소드 예시

import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

public class WriteExample_3 {

	public static void main(String[] args) throws IOException {
		Writer writer = new FileWriter("C:/TEMP/test10.txt");
		
		String str = "만나서 반갑습니다.";
		
		// "반갑습니다."만 출력
		writer.write(str, 4, str.length()-4);
		
		writer.flush();
		writer.close();
	}
}

🔵 실행 결과




📝 2-2. 문자 입력 스트림 : Reader


💡 Reader 클래스의 주요 메소드

리턴 타입메소드설명
intread()1개의 문자를 읽고 읽은 리턴
intread(char[] cbuf)읽은 문자들을 매개값으로 주어진 문자 배열에 저장하고 읽은 문자 수를 리턴
intread(char[] cbuf, int off, int len)len개의 문자를 읽고 매개값으로 문자 배열에서 cbuf[off]부터 len개까지 저장 후 읽은 바이트 수를 리턴
voidclose()입력스트림 닫기

String 타입을 매개변수로 받지 않는다. why? 데이터를 확인하기 힘들다. character 배열(시퀀스) = String


🔴 1. read() 메소드 예시

import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

public class ReaderExample {

	public static void main(String[] args) throws IOException {
		Reader reader = new FileReader("C:/TEMP/test7.txt");
		
		while(true) {
			int data = reader.read();
			if(data == -1) break;
			// 아스키코드를 char로 변환해서 출력
			System.out.println((char)data);
			// 아스키코드 그대로 출력
			System.out.println(data);
		}
		reader.close();
	}
}

🔵 실행 결과

A
65
B
66
C
67


🔴 2. read(byte[] b, int off, int len) 메소드 예시

import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

public class ReaderExample {

	public static void main(String[] args) throws IOException {
		Reader reader = new FileReader("C:/TEMP/test8.txt");
		
		char[] buffer = new char[5];
		
		// buffer[2],buffer[3],buffer[4]로 저장해라
        int readCharNum = reader.read(buffer, 2, 3);
		
		// 데이터가 있다면
		if(readCharNum!=-1) {
			// buffer 전체 출력
			for(int i=0; i<buffer.length; i++) {
				System.out.println(buffer[i]);
			}
		}
		reader.close();
	}
}

read(byte[] b, int off, int len) -> off+len = 배열의 크기


🔵 실행 결과



중
앙
인


참고 : https://terianp.tistory.com/19
https://bamdule.tistory.com/179
https://yooniron.tistory.com/16

0개의 댓글