Java 파트14-1. 입출력 스트림

1. 시작하기 전에


자바에서 데이터는 스트림을 통해 입출력이 된다. 스트림은 단일 방향으로 연속적으로 흘러가는 것을 말한다. 데이터입출력은 키보드, 파일, 프로그램 입력 -> 프로그램 -> 모니터, 파일, 프로그램 출력 이다. 여기서 프로그램의 위치에 따라 사용하는 스트림의 종류가 달라진다. 입력에서 프로그램으로 이동할 때 프로그램이 도착지에 있으므로 입력스트림을 사용해야하고, 출력에서는 프로그램이 출발지에 있으므로 출력 스트림을 사용해야 한다.


2. 입출력 스트림의 종류


java.io 패키지에는 여러 종류의 스트림 클래스를 제공하는데 스트림 클래스는 크게 두 가지 종류로 구분된다.

  • 바이트 기반 스트림 : 그림, 멀티미디어 등의 바이너리 데이터를 읽고 출력할 때 사용
  • 문자 기반 스트림 : 문자 데이터를 읽고 출력할 때 사용

바이트 기반인지 문자 기반인지 확인하려면 최상위 클래스를 보면 된다.

  • 바이트 기반 스트림
    • 최상위 클래스
      • 입력 스트림 : InputStream
      • 출력 스트림 : OutputStream
    • 하위 클래스(예시)
      • XXXInputStream
      • XXXOutputStream
  • 문자 기반 스트림
    • 최상위 클래스
      • 입력 스트림 : Reader
      • 출력 스트림 : Writer
    • 하위 클래스(예시)
      • 입력 스트림 : XXXReader
      • 출력 스트림 : XXXWriter

위와 같이 하위 클래스는 최상위 클래스가 하위 클래스에 접미사로 붙는다.


3. 바이트 출력 스트림


OutputStream은 바이트 기반 출력 스트림의 최상위 클래스로 추상 클래스이고, 모든 바이트 기반 출력 스트림 클래스는 최상위 클래스를 상속받아서 만들어 진다. FileOutputStream, BufferedOutputStream, PrintStream, DataOutputStream 클래스는 모두 OutputStream 클래스를 상속하고 있다. OutputStream 클래스에는 바이트 기반 출력 스트림이 기본적으로 가져야 할 메소드가 정의되어 있다. 다음은 주요 메소드이다.

리턴 타입메소드설명
voidwrite(int b)1byte 출력
voidwrite(byte[] b)매개값으로 주어진 배열 b의 모든 바이트를 출력
voidwrite(byte[] b, int off, int len)매개값으로 주어진 배열 b[off]부터 len개까지의 바이트 출력
voidflush()출력 버퍼에 잔류하는 모든 바이트를 출력
voidclose()출력 스트림 닫기


write(int b)

write(int)는 1바이트만 출력 스트림으로 보내기 때문에 int의 4바이트 중 제일 오른쪽에 있는 1바이트만 출력 스트림으로 보낸다. 출력 스트림은 출력할 바이트를 바로 보내는 것이 아니라 내부 버퍼(저장소)에 우선 저장해 놓는다. flush() 메소드는 이 내부 버퍼에 잔류된 바이트를 모두 출력하는 역할을 한다.

public static void main(String[] args) throws Exception {
        // test1.db로 하는 바이트 기반 파일 출력 스트림 생성
		OutputStream os = new FileOutputStream("C:/Temp/test1.db");
		
		byte a = 10;
		byte b = 20;
		byte c = 30;
		
		os.write(a); // 출력 스트림 버퍼에 저장
		os.write(b);
		os.write(c);
		
		os.flush(); // 전부 출력
		os.close(); // 출력 스트림 닫기
	}


write(byte[] b)와 write(byte[] b, int off, int len)

매개값으로 주어진 배열의 모든 바이트를 출력스트림으로 보낸다. off와 len이 주어지면 b[off]부터 len개의 바이트를 출력 스트림으로 보낸다.

// write(byte[] b)
	public static void main(String[] args) throws Exception {
        // test2.db로 하는 바이트 기반 파일 출력 스트림 생성
		OutputStream os = new FileOutputStream("C:/Temp/test2.db");
		
		byte[] array =  { 10, 20, 30 };
		
		os.write(array); // 전부 출력 스트림으로 넘김
		
		os.flush(); // 전부 출력
		os.close(); // 출력 스트림 닫기
	}
// write(byte[] b, int off, int len)
public static void main(String[] args) throws Exception {
		OutputStream os = new FileOutputStream("C:/Temp/test3.db");
		
		byte[] array =  { 10, 20, 30, 40, 50 };
		
		os.write(array, 1, 3); // 20 30 40 만 보내진다.
		
		os.flush();
		os.close();
	}


4. 바이트 입력 스트림


InputStream은 바이트 기반 입력 스트림의 최상위 클래스로 추상 클래스이다. 모든 바이트 기반 입력 스트림은 InputStream을 상속하고 하위 클래스는 FileInputStream, BufferedInputStream, DataInputStream 클래스가 있다. 주요 메소드는 다음과 같다.

리턴 타입메소드설명
intread()1byte를 읽고 읽은 바이트 리턴
intread(byte[] b)읽은 바이트를 매개값으로 주어진 배열에 읽은 바이트 수 리턴
intread(byte[] b, int off, int len)len개의 바이트를 읽고 매개값으로 주어진 배열에서 b[off]부터 len개까지 저장후 읽은 바이트 수 리턴
voidclose()입력 스트림 닫기


read()

read 메소드는 입력스트림으로부터 1바이트를 읽고 4바이트 int로 리턴한다. 따라서 리턴한 4바이트 중 1바이트에만 데이터가 들어가 있다.

public static void main(String[] args) throws Exception {
        // 위에 예시에서 저장한 test1.db를 이용
        // tset1.db를 입력 스트림으로 열기
		InputStream is = new FileInputStream("C:/Temp/test1.db");
		while(true) {
			int data = is.read(); // 1바이트씩 읽기
			if(data == -1) break; // 더이상 읽을 수 없으면 -1이 반환된다.
			System.out.println(data);
		}
		is.close(); // 닫기
	}


read(byte[] b)와 read(byte[] b, int off, int len)

read(byte[] b)는 입력 스트림으로부터 매개값으로 주어진 배열의 길이만큼 바이트를 읽고 해당 배열에 저장하고 바이트 수를 리턴한다. 실제로 읽은 바이트 수가 배열의 길이보다 적을 경우, 읽은 수만큼만 리턴한다. 무슨 뜻이냐면, 아래 예시에서 buffer의 크기인 100만큼 받고 다음에 받을 때 만약 읽을 것이 50만큼만 남아있다면 50만큼 저장하고 그 뒤에는 전에 받았던 것들이 그대로 남아있게 된다. 하지만 읽은 것은 50만 읽은 것이니 반환 값은 50이 된다.
read(byte[] b, int off, int len)는 그냥 시작점과 받을 개수가 정해진 것이고 read(byte[] b)와 똑같다.

// read(byte[] b)
public static void main(String[] args) throws Exception {
		InputStream is = new FileInputStream("C:/Temp/test2.db");
		
		byte[] buffer = new byte[100];
		
		while(true) {
            // buffer배열의 길이만큼 읽고 buffer에 저장하고 읽은 바이트 수 리턴
			int readByteNum = is.read(buffer); 
			if(readByteNum == -1) break;
			for(int i=0; i<readByteNum; i++) {
				System.out.println(buffer[i]);
			}
		}
		
		is.close();
	}
//read(byte[] b, int off, int len)
public static void main(String[] args) throws Exception {
		InputStream is = new FileInputStream("C:/Temp/test3.db");
		
		byte[] buffer = new byte[5];
		
        // buffer의 인덱스 2부터 2,3,4에만 저장된다.
		int readByteNum = is.read(buffer, 2, 3);
		if(readByteNum != -1) {
			for(int i=0; i<buffer.length; i++) {
				System.out.println(buffer[i]);
			}
		}
		
		is.close();
	}


5. 문자 출력 스트림


Writer은 문자 기반 출력 스트림의 최상위 클래스로 추상클래스이다. 따라서 모든 문자 기반 출력 스트림은 Writer을 상속받아서 만들어지고 하위 클래스로 FileWriter, BufferedWriter, PrintWriter, OutputStreamWriter가 있다. 주요 메소드는 다음과 같다.

리턴 타입메소드설명
voidwrite(int c)매개값으로 한 문자를 보냄
voidwrite(char[] cbuf)매개값으로 주어진 배열의 모든 문자를 보냄
voidwrite(char[] cbuf, int off, int len)매개값으로 주어진 배열에서 cbuf[off]부터 len개까지의 문자를 보냄
voidwrite(String str)매개값으로 주어진 문자열을 보냄
voidwrite(String str, int off, int len)매개값으로 주어진 문자열에서 off순번부터 len개까지의 문자를 보냄
voidflush()버퍼에 잔류하는 모든 문자를 출력
voidclose()출력 스트림 닫기


write(int c)

매개 변수로 주어지는 int 4바이트에서 끝 2바이트(한 개 문자)만 출력스트림으로 보낸다. 예제는 굳이 적지 않겠다. 위에 바이트 출력 스트림과 거의 똑같기 때문이다.


write(char[] cbuf)와 write(char[] cbuf, int off, int len)

바이트 출력 스트림과 다른 점은 문자라는 점 뿐이다.

public static void main(String[] args) throws Exception {
		Writer writer = new FileWriter("C:/Temp/test9.txt");
		
		char[] array = { 'A', 'B', 'C', 'D', 'E' };
		
		writer.write(array, 1, 3); // B C D
		
		writer.flush();
		writer.close();
	}
}


write(String str)와 write(String str, int off, int len)

위에는 문자 하나 씩이지만, 이번에는 문자열 한번에 처리하는 것이다.

public static void main(String[] args) throws Exception {
		Writer writer = new FileWriter("C:/Temp/test8.txt");
		
		String str = "안녕 자바"; 
		
		writer.write(str); // 안녕 자바
        // writer.write(str,3,2); // 자바
		
		writer.flush();
		writer.close();
	}


6. 문자 입력 스트림


Reader는 문자 기반 입력 스트림의 최상위 클래스로 추상 클래스다. 모든 문자 기반 입력 스트림은 Reader 클래스를 상속 받고 하위 클래스는 FileReader, BufferedReader, InputStreamReader이다. 주요 메소드는 다음과 같다.

리턴 타입메소드설명
intread()1개 문자를 읽고 리턴
intread(char[] cbuf)읽은 문자들을 매개값으로 주어진 문자 배열에 저장하고 읽은 문자 수 리턴
intread(char[] cbuf,int off, int len)len개의 문자를 읽고 매개값으로 주어진 문자 배열에서 cbuf[off]부터 len개까지 저장하고 읽은 문자 수 리턴
voidclose()입력 스트림 닫기


read()

입력 스트림으로부터 1개의 문자(2바이트)를 읽고 int(4바이트) 타입으로 리턴한다. 4바이트 중 끝 2바이트에만 문자 데이터가 들어있다. 리턴 받은 후 char 타입으로 형변환 하면 읽을 수 있다.

public static void main(String[] args) throws Exception {
		Reader reader = new FileReader("C:/Temp/test7.txt");
		while(true) {
			int data = reader.read();
			if(data == -1) break;
			System.out.println((char)data); // char타입으로 변환해서 문자 읽기
		}
		reader.close();
	}


int|read(char[] cbuf)와 int|read(char[] cbuf,int off, int len)

다른 것들과 똑같이 사용법은 다를 게 없다.

public static void main(String[] args) throws Exception {
		Reader reader = new FileReader("C:/Temp/test9.txt");
		
		char[] buffer = new char[5];
		
		int readCharNum = reader.read(buffer, 2, 3);
		if(readCharNum != -1) {
			for(int i=0; i<buffer.length; i++) {
				System.out.println(buffer[i]);
			}
		}
		
		reader.close();
	}



본 포스팅은 ‘혼자 공부하는 자바’를 읽고 공부한 내용을 바탕으로 작성하였습니다.


© 2021. By Backtony