티스토리 뷰

JAVA

[Java Study 13] I/O

DevBee 2021. 5. 13. 23:32

I/O란?

Input과 Output의 약자로 입출력을 의미합니다. 간단한 예를 보면 키보드로 텍스트를 입력하고, 모니터로 입력한 텍스트를 출력하는 것을 말합니다.

 

스트림 (Stream) 기반 I/O

자바에서는 파일이나 콘솔 입출력을 직접 다루지 않고 스트림이라는 흐름을 사용합니다.

 

스트림은 실제의 입력이나 출력이 표현되는 데이터의 이상화된 흐름을 의미합니다. 즉, 운영체제에의해 생성되는 가상의 연결고리, 중간 매개체를 의미합니다.

 

 

스트림은 한 방향으로만 통신할 수 있으며, 입력과 출력을 동시에 처리할 수는 없습니다. 따라서 스트림은 사용 목적에 따라 입력 스트림과 출력 스트림으로 구분됩니다.

 

자바에서는 java.io 패키지를 통해 InputStream과 OutputStream을 각각 제공합니다. InputStream과 OutputStream은 바이트 단위 입출력을 위한 최상위 입출력 스트림 클래스입니다.

 

자바에서 스트림 생성이란 이러한 스트림 클래스 타입의 인스턴스를 생성한다는 것입니다.

 

InputStream 클래스에는 read() 메소드가 OutputStream 클래스에는 write() 메소드가 각각 추상 메소드로 포함되어 있습니다.

 

클래스 메소드 설명
InputStream abstract int read() 해당 입력 스트림으로부터 다음 바이트를 읽어들임
int read(byte[] b) 해당 입력 스트림으로부터 특정 바이트를 읽어들인 후, 배열 b에 저장
int read(byte[] b, int off, int len) 해당 입력 스트림에서 len 바이트를 읽어들인 후, 배열 b[off]부터 저장
OutputStream abstract void write(int b) 해당 출력 스트림에 특정 바이트를 저장함
void write(byte[] b) 배열 b의 특정 바이트를 배열 b의 길이만큼 해당 출력 스트림에 저장
void write(byte[] b, int off, int len) 배열 b[off]부터 len 바이트를 해당 출력 스트림에 저장

 

read() 메소드의 경우 더 이상 읽을 다음 바이트가 없는 경우 -1을 반환해야 하는데 반환 타입이 byte인 경우 0부터 255까지의 바이트 정보는 표현할 수 있지만 -1은 표현할 수 없기 때문에 반환 타입을 int로 선언합니다.

 

그 외 InputStream이 가지는 기본 메소드와 OutputStream이 가지는 기본 메소드를 살펴보면 다음과 같습니다.

 

- InputStream 메소드

 

메소드 설명
int available() 현재 읽을 수 있는 바이트 수를 반환합니다.
void close() 현재 열려있는 InputStream을 닫습니다.
void mark(int readlimit) InputStream에서 현재의 위치를 표시합니다.
boolean markSupported() 해당 InputStream에 mark()로 지정된 지점이 있는지 여부를 확인합니다.
void reset() mark()를 마지막으로 호출한 위치로 이동합니다.
long skip(long n) InputStream에서 n 바이트만큼 데이터를 스킵하고 바이트 수를 반환합니다.

 

- OutputStream 메소드

 

메소드 설명
void close() OutputStream을 닫습니다.
void flush() 버퍼에 남아있는 출력 스트림을 출력합니다.

 

Byte와 Character 스트림

Byte 스트림은 byte 단위로 데이터를 전송하며 입출력 대상에 따라 제공하는 클래스가 다릅니다. 그림, 멀티미디어, 문자 등 모든 종류의 데이터를 주고 받을 수 있습니다.

Byte Stream Class

Character 스트림은 오직 문자 데이터만을 주고 받기 위한 스트림입니다.

 

Character Stream Class

 

버퍼 (Buffer) 기반 I/O

버퍼는 데이터를 전송하는 상호간의 장치에서 고속의 장치와 저속의 장치 간의 속도 차이로 인해 저속의 장치가 작업을 수행하는 동안 고속의 장치가 기다려야 하는 현상을 줄여주는 기술입니다.

 

즉, 버퍼는 데이터를 어떤 지점에서 다른 지점으로 전송하는 동안 일시적으로 데이터를 보관하는 메모리 영역을 말합니다.

 

바이트 스트림 클래스의 하위 클래스로는 BufferedInputStream, BufferedOutputStream이 있고, 문자열 스트림 클래스의 하위 클래스로는 BufferedReader, BufferedWriter가 있습니다.

 

채널 (Channel) 기반 I/O

하드웨어 장치, 파일, 네트워크 소켓 또는 프로그램 구성 요소와 같은 하나 이상의 고유한 I/O 작업을 수행할 수 있는 엔티티에 대한 열린 연결을 나타냅니다.

 

채널은 생성 시 열려 있으며 닫히면 닫힌 상태가 유지됩니다. 닫힌 채널에서 I/O 작업을 호출하려고 하면 에러가 발생합니다. 채널이 열려있는지 여부는 isOpen() 메소드를 통해 확인할 수 있습니다.

 

자바의 채널 클래스는 다음과 같습니다.

 

채널 설명
Channel I/O 작업을 위한 최상위 채널 인터페이스
ByteChannel 바이트를 읽고 쓸 수 있는 채널
ReadableByteChannel 채널에서 버퍼로 바이트 읽기
WritableByteChannel 채널에서 버퍼로 바이트 쓰기
ScatteringByteChannel 단일 버퍼가 아닌 일련의 버퍼를 사용한 바이트 읽기 (ReadableByteChannel 확장 인터페이스)
GatheringByteChannel 단일 버퍼가 아닌 일련의 버퍼를 사용한 바이트 쓰기 (WritableByteChannel 확장 인터페이스)
SeekableByteChannel 채널의 현재 위치 및 크기를 쿼리하고 수정하는 방법을 제공하는 ByteChannel의 확장 인터페이스
AsynchronousChannel 비동기 I/O 작업을 지원
AsynchronousByteChannel 비동기적으로 바이트 읽기, 쓰기 가능
NetworkChannel 채널의 소캣을 바인딩하는 방법, 소캣이 바인딩 된 주소, 소캣 옵션을 가져오고 설정하는 방법을 지정
MulticastChannel 인터넷 프로토콜(IP) 멀티캐스트 그룹에 가입하는 방법을 지정
Channels java.io 패키지의 Stream 클래스와 Channel 클래스의 상호작용을 지원하는 정적 메소드를 정의

 

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

기본적으로 키보드에서 입력을 읽고 디스플레이에 출력을 쓰는 스트림입니다. 파일 및 프로그램 간 I/O도 지원하지만 이 기능은 프로그램이 아닌 명령줄 인터프리터에 의해 제어됩니다.

 

Java는 세 가지 표준 스트림을 지원합니다.

 

  • System.in
  • System.out
  • System.err

System.in은 바이트 입력 스트림이고 System.out, System.err는 바이트 출력 스트림입니다.

 

파일 읽고 쓰기

✔️일반적으로 사용되는 작은 파일 한번에 읽고 쓰기

 

작은 크기의 파일이 있는 경우 전체 내용을 한번에 읽으려면 readAllBytes(Path) 또는 readAllLines(Path, Charset) 메소드를 사용하면 됩니다.

 

Path file = ...;
byte[] fileArray;
fileArray = Files.readAllBytes(file);

 

write(Path, byte[], OpenOption...) 또는 write(Path, Iterable< extends CharSequence>, Charset, OpenOption...)를 사용하여 파일에 바이트 또는 줄을 쓸 수 있습니다.

 

Path file = ...;
byte[] buf = ...;
Files.write(file, buf);

 

 

✔️Text 파일을 위한 Buffered I/O 메소드

 

newBufferedReader 메소드는 읽기 위해 파일을 열고 파일의 텍스트를 효율적으로 읽는데 사용할 수 있는 BufferedReader를 반환합니다.

 

Charset charset = Charset.forName("US-ASCII");
try (BufferedReader reader = Files.newBufferedReader(file, charset)) {
    String line = null;
    while((line = reader.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException e) {
    System.err.format("IOException: %s%n", e);
}

 

newBufferedWriter 메소드를 사용하면 BufferedWriter를 이용해 파일에 쓸 수 있습니다.

 

Charset charset = Charset.forName("US-ASCII");
String s = ...;
try (BufferedWriter writer = Files.newBufferedWriter(file, charset)) {
    writer.write(s, 0, s.length());
} catch (IOException e) {
    System.err.format("IOException: %s%n", e);
}

 

 

✔️Unbuffered Streams 및 java.io API와 상호 운용 가능한 메소드

 

읽기 위해 파일을 열려면 newInputStream 메소드를 사용할 수 있습니다. 이 메소드는 파일에서 바이트를 읽기 위해 버퍼링되지 않은 입력 스트림을 반환합니다.

 

Path file = ...;
try (InputStream in = Files.newInputStream(file);
     BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {
    String line = null;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException e) {
    System.err.println(e);
}

 

newOutputStream 메소드를 사용해 파일을 만들거나 파일에 쓸 수 있습니다. 이 메소드는 바이트를 쓰기 위한 파일을 열거나 만들고 버퍼링되지 않은 출력 스트림을 반환합니다.

 

이 메소드는 Optional로 OpenOption 매개변수를 사용합니다. OpenOptional이 별도로 지정되지 않으면 파일이 없는 경우 새로 생성하고 파일이 있다면 잘립니다. 아래 예제의 경우, 파일이 없으면 파일이 생성되고 파일이 있는 경우 첨부용으로 열립니다.

 

import static java.nio.file.StandardOpenOption.*;
import java.nio.file.*;
import java.io.*;

public class LogFileTest {

  public static void main(String[] args) {

    // Convert the string to a
    // byte array.
    String s = "Hello World! ";
    byte data[] = s.getBytes();
    Path p = Paths.get("./logfile.txt");

    try (OutputStream out = new BufferedOutputStream(Files.newOutputStream(p, CREATE, APPEND))) {
      out.write(data, 0, data.length);
    } catch (IOException e) {
      System.err.println(e);
    }
  }
}

 

 

✔️Channel과 ByteBuffers를 위한 메소드

 

스트림 I/O가 한 번에 문자를 읽는 동안 채널 I/O는 한 번에 버퍼를 읽습니다.

 

채널 I/O를 읽고 쓰는 방법은 두 가지가 있습니다.

 

  • newByteChannel(Path, OpenOption...)
  • newByteChannel(Path, Set, FileAttribute...)

newByteChannel 메소드는 SeekableByteChannel 인스턴스를 반환합니다.

 

newOutputStream 메소드에서 사용하는 동일한 열린 옵션이 지원됩니다. 그리고 읽기 및 쓰기를 모두 지원하기 때문에 READ 옵션이 지원됩니다. READ를 지정하면 읽을 채널이 열립니다. WRITE 또는 APPEND를 지정하면 쓸 수 있는 채널이 열립니다.

 

채널을 이용해 파일을 읽고 쓸 수 있습니다.

 

// Defaults to READ
try (SeekableByteChannel sbc = Files.newByteChannel(file)) {
    ByteBuffer buf = ByteBuffer.allocate(10);

    // Read the bytes with the proper encoding for this platform.  If
    // you skip this step, you might see something that looks like
    // Chinese characters when you expect Latin-style characters.
    String encoding = System.getProperty("file.encoding");
    while (sbc.read(buf) > 0) {
        buf.rewind();
        System.out.print(Charset.forName(encoding).decode(buf));
        buf.flip();
    }
} catch (IOException e) {
    System.out.println("caught exception: " + e);

 

참고

- https://velog.io/@ljs0429777/Java-IO

- http://tcpschool.com/java/java_io_stream

- https://develop-im.tistory.com/54

- https://velog.io/@youngerjesus/%EC%9E%90%EB%B0%94-IO-Streams

'JAVA' 카테고리의 다른 글

[Java Study 15] 람다식  (0) 2021.05.15
[Java Study 14] 제네릭  (1) 2021.05.15
[Java Study 12] 애노테이션  (0) 2021.05.13
[Java Study 11] Enum  (0) 2021.05.11
[Java Study 10] 멀티쓰레드 프로그래밍  (0) 2021.05.07
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함