Java Category/Java

[Java] 바이트 & 문자 입출력 스트림

ReBugs 2023. 8. 8.

이 게시글은 이것이 자바다(저자 : 신용권, 임경균)의 책과 동영상 강의를 참고하여 개인적으로 정리하는 글임을 알립니다. 


입출력 스트림

자바는 입력 스트림과 출력 스트림을 통해 데이터를 입출력한다.

스트림은 단 방향으로 데이터가 흐르는 것을 말한다.

  • 입력 스트림 : 입력 장치 -> 프로그램
  • 출력 스트림 : 프로그램 -> 출력 장치

프로그램을 기준으로 데이터가 들어오면 입력스트림, 데이터가 나가면 출력 스트림이 된다.

프로그램이 다른 프로그램과 데이터를 교환하려면 양쪽 모두 입력 스트림과 출력 스트림이 필요하다.

 

어떤 데이터를 입출력하느냐에 따라 스트림은 아래의 두 종류로 구분할 수 있다.

  • 바이트 스트림 : 그림, 멀티미디어, 문자 등 모든 종류의 데이터를 입출력할 때 사용
  • 문자 스트림 : 문자만 입출력할 때 사용
두 스트림 모두 바이트를 전송한다.
또한 바이트 스트림도 문자를 입출력할 수 있지만 문자로 변환하는 과정이 추가적으로 필요하다.

 

자바는 데이터 입출력과 관련된 라이브러리를 java.io 패키지에서 제공하고 있다.

java.io 패키지는 바이트 스트림과 문자 스트림을 아래와 같이 이름으로 구분해서 제공한다.

출처 : 이것이 자바다 유튜브 동영상 강의

바이트 입출력 스트림의 최상위 클래스는 InputStream과 OutputStream이다.

이 클래스를 상속받는 자식 클래스에는 접미사로 InputStream과 OutputStream이 붙는다.

 

문자 입출력 스트림의 최상위 클래스는 Reader와 Writer이다.

이 클래스를 상속받는 하위 클래스에는 접미사로 Reader 또는 Writer가 붙는다.

 

예를 들어 이미지와 같은 바이너리 파일의 입출력 스트림 클래스는 FileInputStream과 FileOutputStream

예를 들어 텍스트 파일의 입출력 스트림 클래스는 FileReader와 FileWriter이다.

출처 : 이것이 자바다 유튜브 동영상 강의

 


 

바이트 출력 스트림

OutputStream은 바이트 출력 스트림의 최상위 클래스로 추상 클래스이다.

모든 바이트 출력 스트림 클래스는 이 OutputStream 클래스를 상속받아서 만들어진다.

출처 : 이것이 자바다 유튜브 동영상 강의

OutputStream 클래스에는 모든 바이트 출력 스트림이 기본적으로 가져야 할 메소드가 정의되어 있다.

출처 : 이것이 자바다 유튜브 동영상 강의

 

1 byte 출력

write(int b) 메소드는 매개값 int(4 byte)에서 끝 1 byte만 출력한다.

매개변수가 int 타입이므로 4 바이트를 모두 보내는 것은 아니다.

따라서 byte의 범위인 -128~127 이외의 수가 들어오면 끝의 한 바이트를 제외한 값은 날라간다.

출처 : 이것이 자바다 유튜브 동영상 강의

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class WriteExample {
	public static void main(String[] args) {
		try {
			OutputStream os = new FileOutputStream("C:/Temp/test1.db"); //폴더는 있어야하고, 파일은 없으면 생성함
				
           		 //1 바이트씩 출력(1 바이트씩 파일에 저장됨)
			byte a = 10;
			byte b = 20;
			byte c = 30;
			
			os.write(a);
			os.write(b);
			os.write(c);
			
			os.flush(); //내부 버퍼에 있는 바이트를 출력하고 버퍼를 비움
			os.close(); //출력 스트림을 닫음, 버퍼를 출력하고 비우는 기능도 있음
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

FileOutputStream 생성자는 주어진 파일을 생성할 수 없으면 IOException을 발생시킨다.

write(), flush(), close() 모두 예외처리를 해주어야 한다.

OutputStream은 내부에 작은 버퍼를 가지고 있다.
write() 메소드가 호출되면 버퍼에 바이트를 우선 저장하고, 버퍼가 차면 순서대로 바이트를 출력한다.
flush() 메소드는 내부 버퍼에 잔류하는 모든 바이트를 출력하고 버퍼를 비우는 역할을 한다.
출력 스트림을 더 이상 사용하지 않을 때에는 close() 메소드를 호출해서 출력 스트림이 사용했던 메모리를 해제해야 한다.
아니면 리소스 자동 닫기로 반드시 스트림을 닫아야 한다.

FileOutputStream은 두 번째 매개값에 true를 주면 새로운 내용을 뒤에 추가시키는 것이고(이어 쓰기), false를 주면 처음부터 다시 쓰게 된다.

 


 

바이트 배열 출력

보통 바이트 배열을 통째로 출력하는 경우가 많다.

write(byte[] b) 메소드는 매개값으로 주어진 배열의 모든 바이트를 출력한다.

출처 : 이것이 자바다 유튜브 동영상 강의

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class WriteExample {
	public static void main(String[] args) {
		try {
			OutputStream os = new FileOutputStream("C:/Temp/test2.db");

			byte[] array = { 10, 20, 30 };

			os.write(array); //배열의 모든 바이트를 출력

			os.flush(); //버퍼에 있는 바이트를 출력하고 버퍼를 비움
			os.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

 

만약 배열의 일부분을 출력하고 싶다면 write(byte[] b, int off, int len) 메소드를 사용하면 된다.

이 메소드는 b[off]부터 len개의 바이트를 출력한다.

출처 : 이것이 자바다 유튜브 동영상 강의

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
	
public class WriteExample {
	public static void main(String[] args) {
		try {
			OutputStream os = new FileOutputStream("C:/Temp/test3.db");

			byte[] array = { 10, 20, 30, 40, 50 };

			os.write(array, 1, 3);

			os.flush();
			os.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

 

 


 

바이트 입력 스트림

InputStream은 바이트 입력 스트림의 최상위 클래스로, 추상 클래스이다.

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

출처 : 이것이 자바다 유튜브 동영상 강의

InputStream 클래스에는 바이트 입력 스트림이 기본적으로 가져야 할 메소드가 정의되어 있다.

출처 : 이것이 자바다 유튜브 동영상 강의

 

1 byte 읽기

read() 메소드는 입력 스트림으로부터 1 byte를 읽고 intI(4 byte) 타입으로 리턴한다.

따라서 4byte중 끝 1 byte에만 데이터가 들어 있다.

 

예를 들어 입력 스트림에서 5개의 바이트가 들어온다면 아래와 같이 read() 메소드로 1 바이트씩 5번 읽을 수 있다.

출처 : 이것이 자바다 유튜브 동영상 강의

 

더 이상 입력 스트림으로부터 바이트를 읽을 수 없다면 read() 메소드는 -1을 리턴한다.

 

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

public class ReadExample {
	public static void main(String[] args) {
		try {
			InputStream is = new FileInputStream("C:/Temp/test1.db");

			while(true) {
				int data = is.read(); //한 바이트씩 읽어옴
				if(data == -1) break; //-1을 받으면 탈출
				System.out.println(data);
			}
			
			is.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
/*
10
20
30
*/

FileInputStream 생성자는 주어진 파일이 존재하지 않을 경우 FileNotFoundException을 발생시킨다.

read(), close() 메소드에서 IOException이 발생할 수 있으므로 두 가지 예외를 모두 처리해야 한다.

 IOException의 자식 자식 클래스가 FileNotFoundException이므로 IOException만 예외 처리해 주어도 된다.

 


 

바이트 배열로 읽기

read(byte[] b) 메소드는 입력 스트림으로부터 주어진 배열의 길이만큼 바이트를 읽고 배열에 저장한 다음 읽은 바이트 수를 리턴한다.

예를 들어 입력 스트림에 5개의 바이트가 들어오면 아래와 같이 길이 3인 배열로 두 번 읽을 수 있다.

출처 : 이것이 자바다 유튜브 동영상 강의

read(byte[] b) 역시 입력 스트림으로부터 바이트를 더 이상 읽을 수 없다면 -1을 리턴한다.

 

많은 양의 바이트를 읽을 때는 read(byte[] b) 메소드를 사용하는 것이 좋다.

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

public class ReadExample {
	public static void main(String[] args) {
		try {
			InputStream is = new FileInputStream("C:/Temp/test2.db");
			
			byte[] data = new byte[100];

			while(true) {
				int num = is.read(data); //최대 100바이트를 읽고, 읽은 바이트는 data에 저장, 읽은 수는 리턴
				if(num == -1) break;

				for(int i=0; i<num; i++) {
					System.out.println(data[i]);
				}
			}

			is.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
/*
10
20
30
*/

 

파일 복사 예제

아래의 예제는 파일 복사이다.

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

public class CopyExample {
	public static void main(String[] args) throws Exception {
		String originalFileName = "C:/Temp/test.jpg"; //복사할 파일
		String targetFileName = "C:/Temp/test2.jpg"; //복사될 파일의 이름과 경로 지정
		
                //입출력 스트림 생성
		InputStream is = new FileInputStream(originalFileName);
		OutputStream os = new FileOutputStream(targetFileName);
		
		byte[] data = new byte[1024];
		while(true) {
			int num = is.read(data);
			if(num == -1) break;
			os.write(data, 0, num);
		}
			
		os.flush();
		os.close();
		is.close();
		
		System.out.println("복사가 잘 되었습니다.");
	}
}
/*
복사가 잘 되었습니다.
*/

test.jpg의 크기는 41.4KB(=42393.6 byte)이고, data 배열의 크기가 1024 이므로 for문을 42번 돌게 된다.
또한 0.4 byte(약 400 byte)는 num이 400정도가 되므로 os.write(data, 0, 400) 으로 처리된다.

복사가 잘 된 것을 확인할 수 있다.

 

Java 9부터 좀 더 편리하게 입력 스트림에서 출력 스트림으로 바이트를 복사하는 transferTo() 메소드가 InputStream에 추가되었다.
byte[] data = new byte[1024];
    while(true) {
        int num = is.read(data);
        if(num == -1) break;
        os.write(data, 0, num);
    }
//////위 코드를 아래의 한줄로 대체 가능
is.transferTo(os);​

 

 


 

문자 입출력 스트림

문자 입출력 스트림으로 Reader와 Writer가 있다.

입출력되는 단위가 문자인 것을 제외하고는 바이트 입출력 스트림과 사용 방법은 동일하다.

 

문자 출력

Writer는 문자 출력 스트림의 최상위 클래스로, 추상 클래스이다.

모든 문자 출력 스트림 클래스는 Writer 클래스를 상속받아서 만들어진다.

출처 : 이것이 자바다 유튜브 동영상 강의

 

Writer 클래스에는 모든 문자 출력 스트림이 기본적으로 가져야 할 메소드가 정의되어 있다.

출처 : 이것이 자바다 유튜브 동영상 강의

Writer는 OutputStream과 사용 방법은 동일하지만, 출력 단위가 문자(char)이다.

또한 문자열을 출력하는 writer(String str) 메소드를 추가로 제공한다.

 

import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

public class WriteExample {
	public static void main(String[] args) {
		try {
			//문자 기반 출력 스트림 생성
			Writer writer = new FileWriter("C:/Temp/test.txt");

			//1 문자씩 출력
			char a = 'A';
			writer.write(a);
			char b = 'B';
			writer.write(b);

			//char 배열 출력
			char[] arr = { 'C', 'D', 'E' };
			writer.write(arr);
           		 //writer.write(arr, 0, arr.length); //위와 같은 코드

			//문자열 출력
			writer.write("FGH");
			
			//버퍼에 잔류하고 있는 문자들을 출력하고, 버퍼를 비움
			writer.flush();
			
			//출력 스트림을 닫고 메모리 해제
			writer.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

 


 

문자 읽기

Reader는 문자 입력 스트림의 최상위 클래스로, 추상 클래스이다.

모든 문자 입력 스트림 클래스는 Reader 클래스를 상속받아서 만들어진다.

출처 : 이것이 자바다 유튜브 동영상 강의

Reader 클래스에는 문자 입력 스트림이 기본적으로 가져야 할 메소드가 정의되어 있다.

출처 : 이것이 자바다 유튜브 동영상 강의

 

public class ReadExample {
	public static void main(String[] args) {
		try {
			Reader reader = null;

			//1 문자씩 읽기
			reader = new FileReader("C:/Temp/test.txt"); //텍스트 파일로부터 문자 입력 스트림 생성
			while(true) {
				int data = reader.read();
				if(data == -1) break;
				System.out.print((char)data);
			}
			reader.close();
			System.out.println();

			//문자 배열로 읽기
			reader = new FileReader("C:/Temp/test.txt"); //텍스트 파일로부터 문자 입력 스트림 생성
			char[] data = new char[100]; // 읽은 문자를 저장할 배열 생성
			while(true) {
				int num = reader.read(data); //읽은 문자는 배열에 저장, 읽은 문자 수는 리턴
				if(num == -1) break;
				for(int i=0; i<num; i++) { //읽은 문자 수만큼 출력
					System.out.print(data[i]);
				}
			}
			reader.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

댓글