I/O 기본2

수호천사임다·2024년 10월 13일

자바

목록 보기
8/15

스트림은 byte만 사용할 수 있으므로, String 같은 문자는 직접 전달할 수 없다는 점이다.
개발자가 번거롭게 다음과 같은 변환 과정을 직접 호출

  • String + 문자 집합 -> byte[]
  • byte[] + 문자 집합 -> String
String writeString = "ABC";
// 문자 -> byte 변환 UTF-8 인코딩
byte[] writeBytes = writeString.getBytes(UTF_8);
    
byte[] bytes = fis.readAllBytes();
// byte -> String UTF-8 디코딩
String readString = new String(bytes, UTF_8);

이렇게 번거로운 변환 과정을 누군가 대신 처리해준다면?

스트림을 문자로

  • OutputStreamWriter: 스트림을 byte 대신에 문자를 저장할 수 있게 지원한다.
  • InputStreamReader: 스트림에 byte 대신에 문자를 읽을 수 있게 지원한다.

OutputStream -> 전부 바이트 단위로 동작
Writer -> 문자라는 뜻

package io.text;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

import static io.text.TextConst.FILE_NAME;
import static java.nio.charset.StandardCharsets.UTF_8;

public class ReaderWriterMainV2 {

  public static void main(String[] args) throws IOException {
    String writeString = "ABC";
    System.out.println("write String:" + writeString);

    // 파일에 쓰기
    FileOutputStream fos = new FileOutputStream(FILE_NAME);
    OutputStreamWriter osw = new OutputStreamWriter(fos, UTF_8);

    osw.write(writeString);
    osw.close();

    // 파일에서 읽기
    FileInputStream fis = new FileInputStream(FILE_NAME);
    InputStreamReader isr = new InputStreamReader(fis, UTF_8);

    StringBuilder content = new StringBuilder();
    int ch;
    while ((ch = isr.read()) != -1) {
      content.append((char) ch);
    }
    isr.close();

    System.out.println("read String: " + content);
  }
}

  • OutputStreamWriter는 문자를 입력 받고, 받은 문자를 인코딩해서 byte[]로 변환
  • OutputStreamWriter는 변환한 byte[]을 전달할 OutputStream과 인코딩 문자 집합에 대한 정보가 필요 -> new OutputStreamWriter(fos, UTF_8)
  • osw.write(writeString)을 보면 String 문자를 직접 전달하는 것을 확인 가능
  • OutputStreamWriter가 문자 인코딩을 통해서 byte[]로 변환하고, 변환 결과를 FileOutputStream에 전달하는 것을 확인할 수 있다.
  • 데이터를 읽을 때는 int ch = read()를 제공한다. (문자 하나인 char형으로 데이터를 받게되지만 실제 반환타입은 int형이므로 char 형으로 캐스팅 해한다.
  • 자바의 char형은 파일의 끝인 -1을 표현할 수 없으므로 int를 반환
  • 데이터를 읽을 때 FileInputStream에서 byte[]을 읽는다. InputStreamReader는 이렇게 많은 byte[]을 문 자인 char로 변경해서 반환. (문자 집합 필요)
    • new InputStreamReader(fis, UTF_8)

문자를 저장하는 건 없다.
컴퓨터는 오로지 바이트만 저장 가능 !
어디선가 반드시 한번은 문자를 바이트로 변환하거나
바이트로 문자를 변환해야 우리가 문자를 볼 수 있다.

그냥 문자를 파일에 저장 X

Reader, Writer

  • 바이트 변환없이 문자를 직접 다룰 수 있다.
    byte를 다루는 클래스

  • byte를 다루는 클래스는 OutputStream, InputStream의 자식.

    • 부모 클래스의 기본 기능도 byte 단위를 다룬다.
    • 클래스 이름 마지막에 보통 OutputStream, InputStream이 붙어있다.
      문자를 다루는 클래스

  • 문자를 다루는 클래스는 Writer, Reader의 자식이다.

    • 부모 클래스의 기본 기능은 String, char 같은 문자를 다룬다.
    • 클래스 이름 마지막에 보통 Writer, Reader가 붙어있다.

    Write, Reader 클래스의 자식들은 writer(String)이 가능하다.

    OutputStreamWriter는 문자를 받아서 byte로 변경한 다음에 byte를 다루는 OutputStream으로 데이터를 전달한 것이다.

모든 파일이나 모든 외부의 데이터 전달은 바이트로 하는 것

package io.text;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

import static io.text.TextConst.*;
import static java.nio.charset.StandardCharsets.*;

public class ReaderWriterMainV3 {

  public static void main(String[] args) throws IOException {
    String writeString = "ABC";
    System.out.println("write String:" + writeString);

    // 파일에 쓰기
    FileWriter fw = new FileWriter(FILE_NAME, UTF_8);
    fw.write(writeString);
    fw.close();

    // 파일에서 읽기
    StringBuilder content = new StringBuilder();
    FileReader fr = new FileReader(FILE_NAME, UTF_8);
    int ch;
    while ((ch = fr.read()) != -1){
      content.append((char) ch);
    }
    fr.close();

    System.out.println("read string = " + content);
  }
}

new FileWriter(FILE_NAME, UTF-8)

  • FileWriter에 파일명과, 문자 집합을 전달.
  • FileWriter는 내부에서 스스로 FileOutputStream을 하나 생성해서 사용
public FileWriter(String fileName, Charset charset) throws IOException {
 	super(new FileOutputStream(fileName), charset);
}

fw.write(writeString)

  • 이 메서드를 사용하면 문자를 파일에 직접 쓸 수 있다.
  • FileWrite 내부에서는 인코딩 셋을 사용해서 문자를 byte로 변경하고, FileOutputStream을 사용해서 파일에 저장.
  • 문자를 직접 파일에 쓰는 것 처럼 느끼지만, 실제로는 내부에서 문자를 byte로 변환한다.
FileOutputStream fos = new FileOutputStream(FILE_NAME);
OutputStreamWriter osw = new OutputStreamWriter(fos, UTF_8);
FileWriter fw = new FileWriter(FILE_NAME, UTF_8);
  • FileWriteFileOutputStream, OutputStreamWrite의 생성을 동시에 할 수 있게 편리하게 만들어주는 것이다.

BufferedReader

  • 문자를 다룰 때는 한 줄(라인) 단위로 다룰 때가 많다.
  • BufferedReader는 한 줄 단위로 문자를 읽는 기능도 추가로 제공.
package io.text;

import java.io.*;

import static io.text.TextConst.FILE_NAME;
import static java.nio.charset.StandardCharsets.UTF_8;

public class ReaderWriterMainV4 {

  private static final int BUFFER_SIZE = 8192;

  public static void main(String[] args) throws IOException {
    String writeString = "ABC\n가나다";
    System.out.println("== Write String");
    System.out.println(writeString);

    // 파일에 쓰기
    FileWriter fw = new FileWriter(FILE_NAME, UTF_8);
    BufferedWriter bw = new BufferedWriter(fw, BUFFER_SIZE);
    bw.write(writeString);
    bw.close();

    // 파일에서 읽기
    StringBuilder content = new StringBuilder();
    FileReader fr = new FileReader(FILE_NAME, UTF_8);
    BufferedReader br = new BufferedReader(fr, BUFFER_SIZE);

    String line;
    while ((line = br.readLine()) != null) {
      content.append(line).append("\n");
    }
    br.close();

    System.out.println("==Read String ==");

    System.out.println(content);
  }
}

br.readLine()

  • 한 줄 단위로 문자를 읽고 String을 반환
  • 파일의 끝(EOF)에 도달하면 null을 반환

기타 스트림

PrintStream

-System.out에서 사용되는 스트림

  • PrintStreamFileOutputStream을 조합하면 마치 콘솔에 출력하듯이 파일에 출력 가능
package io.text.streams;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;

public class PrintStreamEtcMain {

  public static void main(String[] args) throws FileNotFoundException {
    FileOutputStream fos = new FileOutputStream("temp/print.txt");
    PrintStream printStream = new PrintStream(fos);

    printStream.println("hello java!");
    printStream.println(10);
    printStream.println(true);
    printStream.printf("hello %s", "world");
    printStream.close();
  }
}

DataOutputStream

  • 자바의 String, int, double, boolean 같은 데이터 형을 편리하게 다룰 수 있다.
  • 이 스트림과 FileOutputStream을 조합하면 파일에 자바 데이터 형을 편리하게 저장할 수 있다.
package io.text.streams;

import java.io.*;

public class DateStreamEtcMain {

  public static void main(String[] args) throws IOException {
    FileOutputStream fos = new FileOutputStream("temp/data.dat");
    DataOutputStream dos = new DataOutputStream(fos);

    dos.writeUTF("회원A");
    dos.writeInt(20);
    dos.writeDouble(10.5);
    dos.writeBoolean(true);
    dos.close();

    FileInputStream fis = new FileInputStream("temp/data.dat");
    DataInputStream dis = new DataInputStream(fis);

    System.out.println(dis.readUTF());
    System.out.println(dis.readInt());
    System.out.println(dis.readDouble());
    System.out.println(dis.readBoolean());
    dis.close();
  }
}
  • 자바 데이터 타입을 사용하면서, 회원 데이터를 편리하게 저장하고 불러올 수 있다.
  • 이 스트림을 사용할 때는 무조건 저장한 순서대로 읽어야 한다.(순서대로 읽지 않을 경우 잘못된 데이터가 출력될 수 있다.)

정리

종류설명예시
기본 스트림File, 메모리, 콘솔 등 직접 접근하는 스트림FileInputStream, FileOutputStream, FileReader, FileWriter, ByteArrayInputStream, ByteArrayOutputStream
보조 스트림기본 스트림을 도와주는 스트림BufferedInputStream, BufferedOutputStream, InputStreamReader, OutputStreamWriter, DataOutputStream, DataInputStream, PrintStream

기본 스트림 - 단독으로 사용 가능
보조 스트림 - 단독으로 사용 불가, 반드시 대상 스트림 필요

강의자료 출처: 김영한의 실전 자바 - 고급 2편, I/O, 네트워크, 리플렉션

0개의 댓글