Java :: I/O :: 3.3 DataInputStream과 DataOutputStream

김병철·2022년 9월 16일
0

Java

목록 보기
11/20

3.3 DataInputStream과 DataOutputStream

DataInputStream과 DataOutputStream 역시 BufferedInputStream / BufferedOutputStream처럼 FilterInputStream / FilterOutputStream의 자손이다.

DataInputStream은 DataInput 인터페이스를,
DataOutputStream은 DataOutput 인터페이스를.
각각 구현했기 때문에 데이터를 읽고 쓰는데 있어서 byte단위가 아닌, 8가지 기본 자료형의 단위로 읽고 쓸 수 있다.

DataOutputStream은 각 기본 자료형 값을 16진수로 표현하여 저장한다.

각 자료형의 크기가 다르므로, 출력한 데이터를 다시 읽을 때는 출력했을 때의 순서를 염두해 두어야 한다.


# DataInputStream의 생성자와 메서드

  • DataInputStream(InputStream in)
    -> 주어진 InputStream 인스턴스를 기반스트림으로 하는 DataInputStream 인스턴스를 생성한다.

  • readXXXXX()
    -> 각 타입에 맞게 값을 읽어 온다.
    (더 이상 읽을 값이 없으면 EOFException을 발생시킨다.)

    boolean : readBoolean()
    byte : readByte()
    char : readChar()
    short : readShort()
    int : readInt()
    long : readLong()
    float : readFloat()
    double : readDouble()
    int : readUnsignedByte()
    int : readUnsignedShort()

  • void readFully(byte[] b)

  • void readFully(byte[] b, int off, int len)
    -> 입력스트림에서 지정된 배열의 크기만큼 또는 지정된 위치에서 len만큼 데이터를 읽어온다.
    (파일의 끝에 도달하면 EOFException이 발생하고, I/O 에러가 발생하면 IOException이 발생한다.)

  • String readUTF()
    -> UTF-8 형식으로 쓰여진 문자를 읽는다.
    (더 이상 읽을 값이 없으면 EOFException을 발생시킨다.)

  • static String readUTF(DataInput in)
    -> 입력스트림(in)에서 UTF-8 형식의 유니코드를 읽어온다.

  • int skipBytes(int n)
    -> 현재 읽고 있는 위치에서 지정된 숫자(n)만큼을 건너뛴다.


# DataOutputStream의 생성자와 메서드

  • DataOutputStream(OutputStream out)
    -> 주어진 OutputStream 인스턴스를 기반스트림으로 하는 DataOutputStream 인스턴스를 생성한다.

  • writeXXXXX()
    -> 각 자료형에 알맞은 값들을 출력한다.

    void writeBoolean(boolean b)
    void writeByte(int b)
    void writeChar(int c)
    void writeChars(String s)
    void writeShort(int s)
    void writeInt(int l)
    void writeLong(long l)
    void writeFloat(float f)
    void writeDouble(double d)

  • void writeUTF(String s)
    -> UTF 형식으로 문자를 출력한다.

  • void writeChars(String s)
    -> 주어진 문자열을 출력한다.
    (writeChar(int c)메서드를 여러번 호출한 결과와 같다.)

  • int size()
    -> 지금까지 DataoutputStream에 쓰여진 byte의 수를 알려 준다.


# DataOutputStream 기본자료형 파일 출력 예제

  • 코드 :
import java.io.*;

public class Ex15_8 {

	public static void main(String[] args) {
		FileOutputStream fos = null;
		DataOutputStream dos = null;
		
		try {
			fos = new FileOutputStream("test.txt");
			dos = new DataOutputStream(fos);
			dos.writeInt(10);
			dos.writeFloat(20.2f);
			dos.writeBoolean(true);
			
			dos.close();
		} catch(IOException e) {
			e.printStackTrace();
		}
	}//main
}
  • 출력 결과 :

    바이트 출력을 볼 수 있는 ultraedit을 사용하였다.
    위와 같이 4byte로 int값, 4byte로 float값, 1byte에 boolean값이 담겨 있는 것을 확인할 수 있다.


# ByteArrayOutputStream 사용 예제

위 예제를 변경하여 FileOutputStream 대신 ByteArrayOutputStream를 사용했다.

  • 코드 :
import java.io.*;
import java.util.Arrays;

public class Ex15_9 {

	public static void main(String[] args) {
		ByteArrayOutputStream bos = null;
		DataOutputStream dos = null;
		
		byte[] result = null;
		
		try {
			bos = new ByteArrayOutputStream();
			dos = new DataOutputStream(bos);
			dos.writeInt(10);
			dos.writeFloat(20.0f);
			dos.writeBoolean(true);
			
			result = bos.toByteArray();
			String[] hex = new String[result.length];
			
			for(int i=0; i<result.length; i++) {
				if(result[i] < 0) {
					hex[i] = String.format("%02x", result[i]+256);
				}
				else {
					hex[i] = String.format("%02x", result[i]);
				}
			}
			System.out.println("10진수  : "+Arrays.toString(result));
			System.out.println("16진수  : "+Arrays.toString(hex));
			
			dos.close();
		} catch(IOException e) {
			e.printStackTrace();
		}
	}//main
}
  • 출력 결과 :
10진수  : [0, 0, 0, 10, 65, -96, 0, 0, 1]
16진수  : [00, 00, 00, 0a, 41, a0, 00, 00, 01]

앞의 4byte인 0, 0, 0, 10은 writeInt(10)에 의해 출력된 값이고,
그 뒤에 65, 096, 0, 0은 writeFloat(20.0f)에 의해 출력된 값이고,
마지막으로 01은 writeBoolean(true)에 의해 출력된 값이다.

result[i]에 256을 더한 이유는 바이트의 맨 앞자리는 부호를 의미하기 때문이다.
(부호없는 0b11111111 은 255이지만, 부호가 있으면 0b11111111 이 -1이다.)

위와 같이 ByteArrayInputStream/ByteArrayOutputStream을 사용하면 byte단위의 데이터 변환 및 조작이 가능하다.


# DataInputStream 기본자료형 입력 예제

DataOutputStream 예제에서 출력한 파일을 DataInputStream으로 읽는 예제이다.
값을 순서대로 읽어와야 한다.

  • 코드 :
public class Ex15_10 {

	public static void main(String[] args) {
		FileInputStream fis;
		try {
			fis = new FileInputStream("test.txt");
			DataInputStream dis = new DataInputStream(fis);
			
			System.out.println(dis.readInt());
			System.out.println(dis.readFloat());
			System.out.println(dis.readBoolean());
			dis.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}// main
}
  • 출력 결과 :
10
20.2
true

파일로부터 데이터를 읽을 때 데이터의 타입에 맞는 메서드를 순서대로 사용하면 된다.


# DataOutputStream 배열값 파일 출력 예제

int배열에 담긴 값을 DataOutputStream을 이용해서 score.txt 파일로 출력하는 예제이다.

  • 코드 :
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Ex15_11 {

	public static void main(String[] args) {
		int[] score = {100, 90, 95, 85, 50};
		
		FileOutputStream fos;
		try {
			fos = new FileOutputStream("score.txt");
			DataOutputStream dos = new DataOutputStream(fos);
			
			for(int i=0; i<score.length; i++) {
				dos.writeInt(score[i]);
			}
			dos.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}//main
}
  • 출력 결과 :

d, Z, _, U, 2는 int배열에 담긴 값의 아스키코드에 맞는 문자들이다.


# DataInputStream 연속으로 데이터 읽는 예제

EOFException이 발생하지 않게 무한 반복문으로 데이터 읽는 예제

  • 코드 :
import java.io.*;

public class EX15_12 {

	public static void main(String[] args) {
		int sum = 0;
		int score = 0;
		
		FileInputStream fis = null;
		DataInputStream dis = null;
		
		try {
			fis = new FileInputStream("score.txt");
			dis = new DataInputStream(fis);
			
			while(true){
				score = dis.readInt();
				System.out.println(score);
				sum+= score;
			}
		} catch(EOFException e) {
			e.printStackTrace();
		} catch(IOException ie) {
			ie.printStackTrace();
		} finally {
			try {
				if(dis!=null) {
					dis.close();
				}
			} catch(IOException ie) {
				ie.printStackTrace();
			}
		}//try
	}//main
}
  • 출력 결과 :
100
90
95
85
50
java.io.EOFException
	at java.io.DataInputStream.readInt(DataInputStream.java:392)
	at example.com.kh.ch15_IO.EX15_12.main(EX15_12.java:19)

무한반복문으로 데이터를 계속 읽다가 읽을 데이터가 없으면 EOFException을 발생시킨다.
참조변수 dis가 null일 때 close()를 호출하면 NullPointerException이 발생하므로 dis가 null인지 체크한 후에 close()를 호출한다.
가비지 컬렉터가 자원을 자동으로 반환해주지만 그래도 꼭 스트림을 닫아서 자원을 반환하자!


# 위의 예제를 try-with-resources문을 이용하여 변경한 예제

간결해지도록 try-with-resources문을 이용해보자

import java.io.DataInputStream;
import java.io.EOFException;
import java.io.FileInputStream;
import java.io.IOException;

public class Ex15_13 {

	public static void main(String[] args) {
		int sum = 0;
		int score = 0;
		
		try (FileInputStream fis = new FileInputStream("score.txt");
			DataInputStream dis = new DataInputStream(fis)){
			
			while(true){
				score = dis.readInt();
				System.out.println(score);
				sum+= score;
			}
		} catch(EOFException e) {
			System.out.println("점수의 총합은 " +sum+"입니다.");
		} catch(IOException ie) {
			ie.printStackTrace();
		} //try
	}//main
}
  • 출력 결과 :
100
90
95
85
50
점수의 총합은 420입니다.

JDK1.7부터 try-with-resources문을 이용해서 close()를 직접 호출하지 않아도 자동호출되도록 할 수 있다.

profile
keep going on~

0개의 댓글