본문 바로가기
Study/Java_study

Java_study_13 (I/O)

by hjshims 2021. 9. 20.

♨학습내용

더보기

스트림 (Stream) / 버퍼 (Buffer) / 채널 (Channel) 기반의 I/O

InputStream과 OutputStream

Byte와 Character 스트림

표준 스트림 (System.in, System.out, System.err)

파일 읽고 쓰기

 

  • 입출력 이란?

- 입출력(I/O)란 Input과 Output의 약자로 입력과 출력, 간단히 입출력이라 한다.

- 입출력은 컴퓨터 내부 또는 외부 장치와 프로그램간의 데이터를 주고 받는 것을 말한다.

- 키보드로부터 데이터를 입력받거나, System.out.println()을 이용해 모니터 화면에 출력하는 것을 말한다.

 

  • 스트림 (Stream) / 버퍼 (Buffer) / 채널 (Channel) 기반의 I/O
  • 스트림 이란?

- 연속된 데이터의 흐름. 입출력 진행 시 다른 작업을 할 수 없는 블록킹 상태가 된다.

- 입출력을 변경하기 편하고, 동일한 프로그램 구조를 유지할 수 있다.

- 단방향으로 흐르기 때문에 입력과 출력을 동시에 할 수 없다. 

 

  • 버퍼 란? 

- byte, char, int 등 기본 데이터 타입을 저장할 수 있는 저장소. 배열과 마찬가지로 제한된 크기 순서대로 데이터를 저장한다.

- 데이터를 저장하기 위한 것이지만 실제 사용은 채널을 통해 데이터를 주고 받을 때 사용된다.

- 채널을 통해 소켓, 파일 등에 데이터를 전송하거나 읽어올 때 버퍼를 사용함으로써 불필요한 데이터의 양을 줄일 수 있고, 서버의 전체 처리량을 늘릴 수 있다.

 

  • 채널 이란?  

- 데이터가 통과하는 쌍방향 통로. 버퍼와 데이터를 주고 받는다.

- 채널에는 소켓과 연결된 SocketChannel, 파일과 연결된 FileChannel, 파이프와 연결된 Pipe.SinkChannel 과 Pipe.SourceChannel 등이 존재하며, 서버소켓과 연결된 ServerSocketChannel 도 존재한다.

 

  • IO vs NIO

- IO 의 방식으로 각각의 스트림에서 read() 와 write() 가 호출이 되면 데이터가 입력 되고, 데이터가 출력되기전까지, 스레드는 블로킹(멈춤) 상태가 된다.

- 이렇게 되면 작업이 끝날때까지 기다려야 하며, 그 이전에는 해당 IO 스레드는 사용할 수 없게 되고, 인터럽트도 할 수 없다.

- 블로킹을 빠져나오려면 스트림을 닫는 방법 밖에 없다.

 

- NIO(New I/O) 기존 IO의 단점을 보완하기 위해 java 4부터 추가된 패키지이다.

- 블로킹 상태에서는 Interrupt를 이용하여 빠져나올 수 있다.

 

  IO NIO
입출력 방식 스트림 방식 채널방식
버퍼 방식 논버퍼 버퍼
비동기 방식 지원안함 지원
블로킹/ 논블로킹 방식 블로킹 방식만 지원
(동기)
블로킹/ 논블로킹 모두 지원
(동기/ 비동기 모두 지원)

 

※ non-buffer vs. buffer

- IO에서는 출력 스트림이 1바이트를 쓰면 입력 스트림이 1바이트를 읽는다.

- 이런 시스템은 대체로 느리다.

- 하지만 버퍼를 사용해서 한 번에 입력받고 출력하는 것이 가능하므로 성능이 좋아진다.

- IO에서는 버퍼를 제공해주는 보조 스트림인 BufferedInputStream, BufferedOutputStream을 연결해서 사용하기도 한다.

- 하지만 NIO는 기본적으로 버퍼를 사용하기 때문에 IO보다 입출력 성능이 좋다.

- 채널은 버퍼에 저장된 데이터를 출력하고, 입력된 데이터를 버퍼에 저장한다.

 

※ 블로킹 vs. 넌블로킹

- 입력 스트림의 read() 메서드를 호출하면 데이터가 입력되기 전 까지 스레드는 블로킹(대기 상태)가 되므로 아무일도 할 수가 없고 블로킹 상태를 인터럽트하여 빠져나올 수도 없다. write() 메서드도 마찬가지이다.

- NIO는 블로킹, 넌블로킹의 특징을 모두 가지고 있는데 IO와의 차이점은 블로킹 시 인터럽트하여 스레드를 빠져나올 수 있다는 것이다.

- NIO의 넌블로킹은 입출력 작업준비가 완료된 채널만 선택해서 작업 스레드가 처리하기 때문에 작업 스레드가 블로킹 되지 않는다.

 

 


 

 

  • InputStream과 OutputStream
  • InputStream

- 바이트 기반 입력 스트림의 최상위 추상 클래스이다.

- 모든 바이트 기반 입력 스트림은 이 클래스를 상속 받아서 만들어진다.

- 버퍼, 파일, 네트워크 단에서 입력되는 데이터를 읽어오는 기능을 수행한다.

 

  • InputStream의 주요 메소드
메서드 내용
int available() 스트림으로부터 읽어올 수 있는 데이터의 크기를 반환
void close() 닫음으로써 사용하고 있던 자원을 반환
void mark(int readlimit) InputStream에서 현재 위치를 표시 해줌
boolean markSupported() 해당 InputStream에서 mark()로 지정된 지점이 있는지에 대한 여부
abstract int read() 1 byte를 읽어온다. (0~255사이의 값.) 더 이상 읽어올 값이 없으면 -1을 반환
abstract 추상 메서드임으로 InputStream의 자손들은 자신의 상황에 알맞게 구현해야함
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) 만큼 건너뜀

 

InputStream

		try (FileInputStream fis = new FileInputStream(new File(fileName))) {
            int content;
            
	// 돌면서 byte를 읽고 file의 끝에 도달하면 -1을 return한다.
            while ((content = fis.read()) != -1) {
                System.out.println((char)content);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

 

  • OutputStream

- 바이트 기반 출력 스트림의 최상위 추상 클래스이다.

- 모든 바이트 기반 출력 스트림은 이 클래스를 상속 받아서 만들어진다.

- 버퍼, 파일, 네트워크 단으로 데이터를 내보내는 기능을 수행한다.

 

  • 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개 만큼만을 읽어서 출력소스에 씀

 

OutputStream

	try (FileOutputStream outputStream = new FileOutputStream(file, false)) {
            int read;
            byte[] bytes = new byte[DEFAULT_BUFFER_SIZE];
            while ((read = inputStream.read(bytes)) != -1) {
                outputStream.write(bytes, 0, read);
            }
        }

 

 


 

 

  • Byte와 Character 스트림 

- InputStream과 OutputStream은 대표적인 byteStream이다.

- 1 byte 단위로 데이터를 입출력한다.

 

- characterStream은 2 byte단위로 데이터를 전송한다.

- 유니코드는 기본단위가 2 byte로 문자를 입출력할때 characterStream을 쓰는것이 적절하다.

- characterStream은 클래스명에 Reader, Writer가 들어가 있다.

 

- BufferedReader

	// 리소스 폴더로부터 파일을 불러온다.
        InputStream is = InputStreamToReaderExample.class
                            .getClassLoader()
                            .getResourceAsStream("file/abc.txt");

        // BufferedReader -> InputStreamReader -> InputStream

        // try-with-resources, auto close
        String line;
        try (BufferedReader br = new BufferedReader(
                      new InputStreamReader(is, StandardCharsets.UTF_8))) {

            // read line by line
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }

        }

 

- BufferedWriter

try (FileWriter writer = new FileWriter("app.log");
             BufferedWriter bw = new BufferedWriter(writer)) {

            bw.write(content);

        } catch (IOException e) {
            System.err.format("IOException: %s%n", e);
        }

 

 


 

 

  • 표준 스트림 (System.in, System.out, System.err)

- 콘솔을 통해 데이터입력과 콘솔로의 데이터 출력을 의미한다.

- 자바에서는 표준 입출력을 위해 3가지 입출력 스트림 in,out,err를 제공한다. 

- 자바앱의 실행과 동시에 자동으로 생성되기 때문에 별도의 스트림없이 사용이 가능하다.

 

  • System.in 

- 콘솔로부터 데이터를 입력받는데 사용된다.

  • System.out

- 콘솔로 데이터를 출력하는데 사용된다.

  • System.err

- 콘솔로 데이터를 출력하는데 사용된다.

- 버퍼링 지원을 하지 않는다.

 

 


 

 

  • 파일 읽고 쓰기
  • 파일 읽기 (read)

- FileReader 객체와 BufferedReader객체가 사용된다.

 

String fileName = "C:/test.txt" // 읽어들일 파일명

FileReader fileReader = new FileReader(fileName);
BufferedReader reader = new BufferedReader(fileReadrer);

String line = null;
while((line = reader.readLine()) != null) { // readLine() 은 한줄 단위로 읽어들임
   System.out.println(line);
}

reader.close();

 

public class FileReadTest {
    public static void main(String[] args) {
        //파일 객체 생성
        File file = new File("/Users/zayson/Documents/spring study/sample.rtf");
        try {
            //입력 스트림, 입력 버퍼 생성
            FileReader fileReader = new FileReader(file);
            BufferedReader br = new BufferedReader(fileReader);
            String line = "";

            //한줄 씩 읽기
            while((line = br.readLine())!=null) {
                System.out.println(line);
            }
            br.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

 

 

  • 파일 쓰기 (write) 

- FileWriter객체와 BufferedWriter객체가 사용된다.

 

String fileName = "C:/test.txt" // 생성할 파일명
String str = "abc" // 작성할 문자열

FileWriter fileWriter = new FileWriter(fileName);
BufferedWriter out = new BufferedWriter(fileWriter);
out.write(str); // 쓰기
out.newLine(); // 줄바꾸기
out.close();

 

public class FileWriteTest {
    public static void main(String[] args) {
        try {
            //파일 객체 생성
            File file = new File("/Users/zayson/Documents/spring study/write_test.txt");
            BufferedWriter bw = new BufferedWriter(new FileWriter(file));

            if (file.isFile() && file.canWrite()) {
                //쓰기
                bw.write("Write 1 line");
                bw.newLine();
                bw.write("두번째 라인 추가");
                bw.newLine();
                bw.write("마지막 확인");
                bw.close();
            }
        } catch (IOException e) {
            System.out.println(e);
        }
    }
}

 

 

 

 

 

 

'Study > Java_study' 카테고리의 다른 글

Java_study_15 (람다식)  (0) 2021.09.27
Java_study_14 (제네릭)  (0) 2021.09.20
Java_study_12 (애노테이션)  (0) 2021.09.14
Java_study_11 (Enum)  (0) 2021.09.07
Java_study_10 (멀티쓰레드 프로그래밍)  (0) 2021.08.30