자바(Java)

[자바] 자바 I/O

2023. 10. 14. 23:26
목차
  1. I/O 입출력
  2. 스트림(Stream)/ 버퍼(Buffer)/ 채널(Channel) 기반의 I/O
  3. 바이트 기반 스트림
  4. 문자 기반 스트림
  5. 표준 스트림 (System.in, System.out, System.err)
  6. 파일 읽고 쓰기
  7. 참고

I/O 입출력

I/O는 input, output의 약자로 입출력 통칭하는 용어로 I/O라고 부르며 읽을때는 Input, 파일쓰거나 외부전송은 Output이다(JVM기준) 한마디로 프로그램간의 데이터를 주고 받는것을 말한다

  • 키보드로부터 데이터를 입력받는것
  • print문을 이용해 화면에 출력하는것

 

 


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

Stream

  • Stream
    • 데이터를 운반하는데 사용되는 연결통로
    • 자료의 입출력을 도와주는 중간 매개체 역할을 한다. 응용 프로그램과 입출력 장치를 연결하는 소프트웨어 모듈
    • 단방향: 입력, 출력 다안하고 하나만 가능하다(따라서 동시에 수행할려면 스트림이 2개 필요하다), 추가로 도달하는것은 큐를 생각하면 됨(선입선출)

 

 

Buffer

  • 데이터를 전송하는 상호간의 장치에서 고속의 장치와 저속의 장치 간의 속도 차이로 인해 저속의 장치가 작업을 추리하는 동안 고속의 장치가 기다려야하는 현상을 줄여준다.
  • Buffer을 사용하는 이유
    • 하드디스크 속도는 매우 느리다
    • 하드디스크 뿐 아니라 키보드, 모니터와 같은 외부장치의 속도는 시간이 많이걸린다. → 중간에 메모리버퍼를 둬서 데이터를 한데 묶어서 이동시키는 것이 효율적이고 빠르다.(엘레베이터가 하나씩 데이터를 옮기는거라 한번에 여러명을 옮기는 것을 비교하면 될듯)

 

 

 

 

Channel

  • 기존의 Stream은 blocking방식과 Non-Buffer의 특징으로 인해 입출력 속도가 느리다. ⇒ 자바 4부터 NIO(New Input Output)가 java.nio패키지에 포함되어 등장, Channerl이 그 NIO의 기본 입출력 방식이다.(buffer방식)
  • 양방향의 통로이다. 양방향이라 input/output 구분안한다
  • 과도한 스레드 생성을 피하고 스레드를 효과적으로 재사용한다
  • NIO는 불특정 다수의 클라이언트를 연결하거나 하나의 입출력 처리작업이 오래 걸리지 않는 경우에 사용하는것이 좋고, IO는 연결 클라이언트 수가 적고 전송되는 데이터가 대용량이면서 순차적으로 처리될 필요성이 있는경우 유리(상황에 맞게 사용해라)

 

Blocking vs non-blocking

  • IO는 블로킹된다
    • 입력스트림의 read()메소드를 호출하면 데이터가 입력되기 전까지 Thread는 블로킹(대기상태)가 된다.
    • 출력스트림의 write()메소드를 호출하면 데이터가 출력되기 전까지 Trhead는 블로킹된다
  • NIO는 블로킹과 넌 블로킹 특징을 모두 가진다
    • NIO는 입출력 작업준비가 완료된 채널만 선택해서 작업 Thread가 처리하기때문에
      Thread가 블로킹 되지 않는다.

 

 


바이트 기반 스트림

InputStream과 OutputStream

  • InputStream과 OutputStream은 모든 바이트기반 스트림의 조상이다.

 

바이트 기반 스트림

  • 바이트 기반 스트림 - InputStream과 OutputStream 사용
    • mark()와 reset()메서드를 통해 이미 읽은 데이터를 되돌려 다시 읽을 수 있다
    • read()메서드를 구현해놔야 read(byte[] b, int off, int len)입력 메서드를 사용할 수가 있다.
    • 모든 작업을 마치고 난후에는 close()호출해서 닫아주어야한다(물론 사용하고 닫지않은 스트림을 JVM이 닫아주기는 하지만 그래도 혹시 모르니ㅇㅇ)
    • ByteArrayInputStream과 ByteArrayOutputStream
      • 메모리, 즉 바이트 배열에 데이터를 입출력 하는데 사용되는 스트림이다
      • Input 데이터를 Write으로 복사해 출력한다.(예제 코드 참고)
        • data에 read()호출한 반환값을 저장한다. 이후 data에 저장된 값이 -1인지 아닌지를 비교하여 반복문을 중단할지 지속할지 정한다
      • 사용 이유: 배열을 이용한 입출력은 작업의 효율을 증가시키므로 가능하면 입출력 대상에 따라 알맞은 크기의 배열 사용이 좋다
    • FileInputStream과 FileOutputStream
      • 파일 입출력을 하귀 위한 스트림
      • 다양한 생성자
        • FileInputStream(String name), FileInputStream(File file), FileInputSteram(FileDescriptor fdObj) 등
      • 텍스트 파일을 다루는 경우 byte기반 스트림 보다는 문자기반의 스트림인 FileReader/FileWriter를 사용하는 것이 더 좋다.

 

바이트 기반 보조 스트림

더 다양한 보조스트림 존재

 

  • 말그대로 보조다 보니 스트림 먼저 생성후 보조스트림 생성해야한다
  • 예시로 기반 스트림 생성후 그걸 이용해서 보조스트림을 생성하면 된다
  • 보조스트림 종류
  • FilterInputStream과 FilterOutputStream
    • InputStream/OutputStream의 자손이면서 모든 보조스트림의 조상이다.(그림 그리면 좋을듯)
    • 보조스트림은 자체적으로 입출력 수행불가이므로 생성할때 InputStream과 OutputStream 선언해줘야함
    //protected이기 때문에 상속을 통해서 오버라이딩 되어야함
    protected FilterInputStream(InputStream in)
    public FilterOutputStream(OutputStream out)
  • BufferedInputStream과 BufferedOutputStream
    • 입출력 효율을 높이기 위해 버퍼를 사용하는 보조스트림이다.
    • 한번에 가져올 수있는 데이터 크기로 지정하면 좋음(약 8K)
    • 내부 버퍼로 읽음
    • OutpustStream은 반대로 버퍼가 가득차면, 모든 내용을 출력소스에 출력하고 비움 → 따라서 버퍼가 가득차지않은 상태로 프로그램 종료되면 문제 발생 → close()나 flush()호출해서 남아있는 모든 내용 출력되도록 해야함(정확히 BufferedOutputStream을 close해줘야함 FileOutputStream close()한다고 달라지는게 없음) → 반대로 보조스트림에서의 close()는 부모인 FileOutputStream()에서 close()를 호출시킴
  • DataInputStream과 DataOutputStream
    • 데이터를 읽고 쓰는데 있어서 byte의 단위가 아닌 8가지 기본 자료형 사용가능하다.(자료형 값을 16진수로 표현하여 저장함)
      • boolean, byte, char, short, int, long, float, double
    • 한가지 주의해야할것은 bit는 맨앞을 부호로 인식해 -128~127로 인식한다. 따라서 256을 더해줘서 출력해야한다.
    • 추가로 여러가지 종류의 자료형으로 출력한 경우, 읽을 때는 반드시 쓰인 순서대로 읽어야 한다
    • readInt, read…()과 같이 읽어올 데이터의 타입에 맞는 메서드만 사용하면 됨(문자로 데이터를 지정하면 다시 읽을때 실제값으로 변환해야하는 과정을 또 거쳐야하는데 그런 번거로움 없음 → DataStream의 장점)
    • DataSystem관련해서 null일대 close()를 호출하면 NullPointerException이 발생하므로 if를 사용해서 null인지 체크해야함. 추가로 close()는 IOException을 발생시킬 수 있으므로 try-catch블럭으로 감싸줘야함 ⇒ 1.7부터는 try-with-resources문을 이용해서 close()를 직접 호출하지않아도 자동호출 됨, 훨씬 간결해짐
  • SequenceInputStream
    • 여러 개의 입력스트림을 연속적으로 연결 후 하나의 스트림으로 데이터를 읽음
    • 큰 파일을 여러개의 작은 파일로 나누었다가 하나의 파일로 합치는 것과 같은 작업 수행시 좋음
  • PrintStream
    • 데이터를 기반스트림에 다양한 형태로 출력할 수 있는 print, println, printf와 같은 메서드 오버로딩하여 제공
    • 자바 1.1부터 더 향상된 기능의 문자 기반 스트림인 PrintWriter가 추가되었지만 익숙한 PrintStream을 사용자들이 많이 사용한다. 하지만 다양한 언어의 문자를 처리하는데 적합한 PrintWriter를 사용하는게 좋다.
    • print()나 println()을 이용해서 출력하는중에 IOExcepiton이 발생하면 checkError()를 통해서 인지가능하다. 예외를 던지지 않고 내부에서 처리되는데 이유는 매우 자주 사용되기 때문이다.
    • 자바 1.5부터 printf()가 추가됨, C언어와 같이 편리한 형식화된 출력을 지원
      • ex. %d(10진수), %o(8진수), %x(16진수), %c(문자), %s(문자열), f(실수) 등

 

 

 


문자 기반 스트림

문자 기반 스트림 - Reader, Writer

  • 바이트기반은 입출력 단위가 1byte, 자바는 char형이 2byte기때문에 바이트 기반의 스트림으로는 2byte처리가 어렵다 → 이점을 보완하고자 문자 기반 스트림 제공 및 사용
  • 종류 관련해서는 바이트기반 스트림의 이름에서 InputStream은 Reader로 OutputStream은 Writer로만 바꾸면 된다, 추가로 달라진점은 byte 배열대신 char배열 사용한다는 것과 추상메서드가 달라짐 ※프로그래밍 관점에서 read()를 추상메서드로 하는것보다 int read(char[] cbuf, int off, int len)을 추상메서드하는게 더 바람직함

 

문자 기반 보조 스트림

  • 사용목적은 바이트 보조 스트림과 동일함(이름만 ~Reader, ~Writer 로 되어있음)
  • 바이트기반 스트림과 문자기반 스트림의 사용방법은 거의 같아서 설명 간략하게(바이트기반에 자세히 나와있음)
  • PipedReader와 PiepedWriter
    • 쓰레드간에 데이터를 주고받을 때 사용된다. 다른 스트림과 달리 입력, 출력스트림이 하나의 스트림으로 연결되어있다
    • 따라서 한쪽 스트림만 닫아도 나머지도 자동으로 닫힌다
    • 사용하기전에 쓰레드와 PipedReader, PipedWriter를 연결해야한다는것만 유의
  • StringReader와 StringWriter
    • StringWirter에서 출력하는 데이터는 내부의 StringBuffer에 저장됨
      • 관련 메서드: StringBuffer getBuffer(), String toString()

 

 


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

  • 표준입출력
    • 종류
      • System.in: 콘솔로부터 데이터를 입력받는데 사용
      • System.out: 콘솔로 데이터를 출력하는데 사용
      • System.err: 콘솔로 데이터를 출력하는데 사용
    • 실행과 동시에 사용할 수 있게 자동생성(별도의 스트림 없이 사용 가능)
    • 콘솔 입력은 버퍼를 가지고 있기 때문에 Backspace키를 이용해서 편집이 가능하다. 따라서 입력의 끝을 알리는 Enter키나 ^z만 안 누르면 계속 입력가능하다.
  • 표준입출력의 대상 변경 - setOut(), setErr(), setIn()
    • 입출력 콘솔이외의 다른 입출력 대상으로 변경하는 것이 가능하다.

 

 


파일 읽고 쓰기

  • 기본적이면서 가장 많이 사용되는 입출력 대상이기 때문에 중요하다
  • 파일 찾을때 File클래스보다 1.7이상버전에서는 Files클래스를 사용하는게 더 효율적인데 이유는 아래와 같다
    • File은 파일을 확장자로 찾아버린다. test.jpg라는 폴더가 있을때 Files는 구분해서 찾지만 File은 그러지 못한다
  • 다양항 File안의 멤버 변수들을 이용해서 파일의 경로와 구분자 등의 정보를 여러방법으로 출력할수 있다.(추가로 하나의 파일에는 하나의 경로만 가능하다. 절대경로가 여러개일 수 없다.)
  • 추가로 경로말고 기본적인 여러 메소드를 통해 파일에 많은 작업을 할 수 있다. 간단하게는 생성, 삭제부터 깊게 들어가면 파일속성변경, URL변환후 반환, 마지막 수정시간 수정 등

 

file.txt 파일 생성하고 내용 작성

FileChannel fileChannel = FileChannel.open(
	Paths.get("C:/Temp/file.txt"),
	StandardOpenOption.CREATE_NEW,
	StandardOpenOption.WRITE
);

 

 

file.txt 파일을 읽고, 쓸 수 있도록 생성

FileChannel fileChannel = FileChannel.open(
	Paths.get("C:/Temp/file.txt"),
	StandardOpenOption.CREATE_NEW,
	StandardOpenOption.WRITE
);

 

NIO

NIO란 자바 4부터 등장한 새로운 입출력 New Input Output이라는 뜻이다. 다양한 버퍼 클래스를 
사용 가능하게 해주며 자바 7부터 등장한 NIO.2 API는 자바 IO와 NIO 사이의 일관성 없는 클래스 설계를

바로 잡고 비동기 채널 등의 네트워크 지원을 대폭 강화했다.

FileChannel과 StandardOpenOption등을 사용할려면  java.nio 패키지를 사용해야한다.

nio 패키지안에 존재하는 다양한 클래스들

 

 

IO와 NIO의 차이점

NIO참고 블로그

 

 

 

 

FileChannel을 이용하여 파일 생성하기

import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class Main {
    public static void main(String[] args) {
        System.out.println("hi");
        Path path = Paths.get("C:\\Users\\kimjisu\\Desktop\\알고리즘 스터디\\자바\\test.txt");

        try(FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.CREATE, StandardOpenOption.WRITE)){

            String data = "hi java";
            Charset charset = Charset.defaultCharset();

            ByteBuffer byteBuffer = charset.encode(data);
            int byteCount = fileChannel.write(byteBuffer);

            System.out.println("file.txt : " + byteCount + " bytes written");


        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

결과

파일이 알맞은 path에 잘 생성된 것을 볼 수 있다

 

 

StandardOpenOption에서 액세스 권한을 안줄경우 발생하는 오류(Write 엑세스를 뺄경우)

※WindowsException: 윈도우 운영체제에서 실행될때, 발생하는 문제를 나타내는 예외이며 
우리가 배웠던 Java의 내장 Exception이랑은 다름

 

 

 

 

 


참고

자바의 정석(저자: 남궁성)

 

https://five-cosmos-fb9.notion.site/I-O-af9b3036338c43a8bf9fa6a521cda242

 

 

 

저작자표시

'자바(Java)' 카테고리의 다른 글

[자바] 람다식(lamda)  (3) 2023.11.09
[자바] 제네릭(Generic)  (1) 2023.11.02
[자바] 자바 어노테이션  (1) 2023.10.05
[자바] 자바 Enum  (0) 2023.09.27
[자바] 자바 멀티쓰레드 프로그래밍  (0) 2023.09.21
  1. I/O 입출력
  2. 스트림(Stream)/ 버퍼(Buffer)/ 채널(Channel) 기반의 I/O
  3. 바이트 기반 스트림
  4. 문자 기반 스트림
  5. 표준 스트림 (System.in, System.out, System.err)
  6. 파일 읽고 쓰기
  7. 참고
'자바(Java)' 카테고리의 다른 글
  • [자바] 람다식(lamda)
  • [자바] 제네릭(Generic)
  • [자바] 자바 어노테이션
  • [자바] 자바 Enum
Ash_jisu
Ash_jisu
JisuStoryAsh_jisu 님의 블로그입니다.
Ash_jisu
JisuStory
Ash_jisu
전체
오늘
어제
  • 분류 전체보기 (135)
    • 알고리즘 (68)
      • 자바 (68)
      • C++ (0)
    • 자바(Java) (18)
    • 스프링 (7)
      • 테스트 (3)
    • 데이터베이스 (3)
      • SQL (7)
      • JPA (1)
      • ElasticSearch (3)
    • CS (3)
    • 배포, 운영 (6)
      • Infra (3)
    • 디자인 패턴 (8)
    • 기타 (5)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 백준
  • 자바
  • dp
  • 자바 #그리디
  • BFS
  • 자바 #백준
  • 백준 #DP
  • 백준 #BFS
  • db
  • 알고리즘 #다익스트라
  • 배포
  • API테스트 #Postman
  • Elasticsearch #Testcontainer #Test
  • 알고리즘 #bfs
  • Elasticsearch #최적화
  • 코딩테스트
  • java
  • swea #구현
  • bfs #알고리즘
  • 프로그래머스 #알고리즘

최근 댓글

최근 글

hELLO · Designed By 정상우.
Ash_jisu
[자바] 자바 I/O
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.