Chap 15. 자바 입출력 [Do it! 자바 프로그래밍 입문]

doriskim·2022년 12월 29일
0

*본 내용은 [Do it! 자바 프로그래밍 입문] 책과 강의를 보고 공부하면서 요점 정리한 내용입니다.

◼ 자바 입출력과 스트림

✔ 스트림이란?

  • 네트웍에서 자료의 흐름이 물과 같다는 의미에서 유래
  • 다양한 입출력 장치에 독립적으로 일관성 있는 입출력을 제공하는 방식
  • 입출력이 구현되는 곳: 파일 디스크, 키보드, 마우스, 메모리 네트웍 등

✔ 스트림의 구분

  • 대상 기준
    입력 스트림 / 출력 스트림

  • 자료의 종류
    바이트 스트림 / 문자 스트림

  • 기능
    기반 스트림 / 보조 스트림

✔ 입력 스트림과 출력 스트림

  • 입력 스트림: 대상으로부터 자료를 읽어들이는 스트림
  • 출력 스트림: 대상으로 자료를 출력하는 스트림
  • 입력 스트림과 출력 스트림을 동시에 쓰지 않는다.
  • 스트림의 종류
종류예시
입력 스트림FileInputStream, FileReader, BufferedInputStream, BufferedReader 등
출력 스트림FileOutputStream, FileWriter, BufferedOutputStream, BufferedWriter 등

✔ 바이트 단위 스트림과 문자 단위 스트림

  • 바이트 단위 스트림: 동영상, 음악 파일 등을 읽고 쓸 때 사용
  • 문자 단위 스트림: 바이트 단위로 자료를 처리하면 문자는 깨짐
    2바이트 단위로 처리하도록 구현된 스트림
  • 스트림의 종류
종류예시
바이트 스트림FileInputStream, FileOutputStream, BufferedInputStream, BufferedOutputStream 등
문자 스트림FileReader, FileWriter, BufferedReader, BufferedWriter 등

✔ 기반 스트림과 보조 스트림

  • 기반 스트림: 대상에 직접 자료를 읽고 쓰는 기능의 스트림
  • 보조 스트림: 직접 읽고 스는 기능은 없이 추가적인 기능을 더해주는 스트림
  • 보조 스트림은 직접 읽고 쓰는 기능은 없으므로 항상 기반 스트림이나 또 다른 보조 스트림을 생성자 매개변수로 포함함
  • 스트림 종류
종류예시
기반 스트림FileInputStream, FileOutputStream, FileReader, FileWriter 등
보조 스트림InputStreamReader, OutputStreamWriter, BufferedInputStream, BufferedOutputStream 등

◼ 표준 입출력

✔ 표준 입출력

  • 입출력에 관련한 코드를 구현하면 예외 처리를 해줘야 함
  • System 클래스의 표준 입출력 멤버
public class System{
	public static PrintStream out;
    public static InputStream in;
    public static PrintStream err;
}
  • System.out
표준 출력(모니터) 스트림
System.out.println("에러메시지");
  • System.in
표준 입력(키보드) 스트림
int d = System.in.read(); // 한 바이트 읽어내기
  • System.err
표준 에러 출력(모니터) 스트림
System.err.println("데이터");

✔ System.in 사용하기 예제

여러 개의 문자를 엔터를 누를 때까지 입력 받기

SystemInTest.java

package stream.inputstream;

import java.io.IOException;

public class SystemInTest {

	public static void main(String[] args) {

		System.out.println("알파벳 여러 개를 쓰고 엔터를 누르세요");
		int i = 0;
		try {
			while((i = System.in.read()) != '\n') { // System.in이 한 바이트씩 읽는다.
				System.out.println((char)i);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}

실행 결과

알파벳 여러 개를 쓰고 엔터를 누르세요
qwerty // 입력
q
w
e
r
t
y

✔ 그 외 입력 클래스 - Scanner 클래스

  • java.util 패키지에 있는 입력 클래스
  • 문자뿐 아니라 정수, 실수 등 다른 자료형도 읽을 수 있음
  • 여러 대상에서 자료를 읽을 수 있음(콘솔, 파일 등)
  • System.in보다 자주 사용
  • 생성자
생성자설명
Scanner(File source)파일을 매개변수로 받아 Scanner를 생성합니다.
Scanner(InputStream source)바이트 스트림을 매개변수로 받아 Scanner를 생성합니다.
Scanner(String source)String을 매개변수로 받아 Scanner를 생성합니다.
  • 메서드
메서드설명
boolean nextBoolean()boolean 자료를 읽습니다.
byte nextByte()한 바이트 자료를 읽습니다.
short nextShort()short 자료형을 읽습니다.
int nextInt()int 자료형을 읽습니다.
long nextLong()long 자료형을 읽습니다.
float nextFloat()float 자료형을 읽습니다.
double nextDouble()double 자료형을 읽습니다.
String nextLine()문자열 String을 읽습니다.

ScannerTest.java

package stream.others;

import java.util.Scanner;

public class ScannerTest {

	public static void main(String[] args) {
		
		Scanner scanner = new Scanner(System.in);
		
		String name = scanner.nextLine();
		int num = scanner.nextInt();
		
		System.out.println(name);
		System.out.println(num);

	}

}

실행 결과

가나다 // 입력
12345 // 입력
가나다
12345

✔ 그 외 입력 클래스 - Console 클래스

  • System.in을 사용하지 않고 콘솔에서 표준 입력을 할 수 있음
  • 이클립스와는 연동되지 않음
  • command 창에서 입력함
  • 메서드
메서드설명
String readLine()문자열을 읽습니다.
char[] readPassword()사용자에게 문자열을 보여주지 않고 읽습니다.
Reader reader()Reader 클래스를 반환합니다.
PrintWriter writer()PrinterWriter 클래스를 반환합니다.

◼ 바이트 단위 스트림

✔ InputStream

  • 바이트 단위 입력용 최상위 스트림
  • 추상 메서드를 포함한 추상 클래스로 하위 클래스가 구현하여 사용
  • 주요 하위 클래스
스트림 클래스설명
FileInputStream파일에서 바이트 단위로 자료를 읽습니다.
ByteArrayInputStreamByte 배열 메모리에서 바이트 단위로 자료를 읽습니다.
FilterInputStream기반 스트림에서 자료를 읽을 때 추가 기능을 제공하는 보조 스트림의 상위 클래스입니다.
(보조 스트림은 '15-5 보조 스트림'에서 자세히 설명합니다.
  • 메서드
메서드설명
int read()입력 스트림으로부터 한 바이트의 자료를 읽습니다. 읽은 자료의 바이트 수를 반환합니다.
int read(byte b[])입력 스트림으로부터 b[]크기의 자료를 b[]에 읽습니다. 읽은 자료의 바이트 수를 반환합니다.
int read(byte b[], int off, int len)입력 스트림으로부터 b[]크기의 자료를 b[]의 off 변수 위치부터 저장하여 len만큼 읽습니다.
읽은 자료의 바이트 수를 반환합니다.
void close()입력 스트림과 연결된 대상 리소스를 닫습니다.
(예: FileInputStream인 경우 파일 닫음)

FileInputStreamTest.java

package stream.inputstream;

import java.io.FileInputStream;
import java.io.IOException;

public class FileInputStreamTest {

	public static void main(String[] args) {

		FileInputStream fis = null;
        int i = 0;
		try {
			fis = new FileInputStream("input.txt");
			while((i = fis.read()) != -1) {
				System.out.print((char)i);
            }
		} catch(IOException e) {
			System.out.println(e);
		} finally {
			try {
				fis.close();
			} catch(IOException e) {
				System.out.println(e);
			} catch(NullPointerException e) {
				System.out.println(e);
			}
		}
		System.out.println("end");
		
	}
}

input.txt

ABC

실행 결과

ABCend

FileInputStreamTest2.java

package stream.inputstream;

import java.io.FileInputStream;
import java.io.IOException;

public class FileInputStreamTest2 {

	public static void main(String[] args) {
		
		try(FileInputStream fis = new FileInputStream("input2.txt")){
			byte[] bs = new byte[10];
			int i;
			while((i = fis.read(bs)) != -1) {
				for(int j = 0; j < i; j++) {
					System.out.println((char)bs[j]);
				}
				System.out.println();
			}
		} catch(IOException e) {
			System.out.println(e);
		}

	}

}

input2.txt

ABCDEFGHIJKLMNOPQRSTUVWXYZ

실행 결과

ABCDEFGHIJ
KLMNOPQRST
UVWXYZ

✔ OutputStream

  • 바이트 단위 출력용 최상위 스트림
  • 추상 메서드를 포함한 추상 클래스로 하위 클래스가 구현하여 사용
  • 주요 하위 클래스
스트림 메서드설명
FileOutputStream바이트 단위로 파일에 자료를 씁니다.
ByteArrayOutputStreamByte 배열에 바이트 단위로 자료를 씁니다.
FilterOutputStream기반 스트림에서 자료를 쓸 때 추가 기능을 제공하는 보조 스트림의 상위 클래스입니다.
  • 메서드
메서드설명
void write(int b)한 바이트를 출력합니다.
void write(byte[] b)b[] 배열에 있는 자료를 출력합니다.
void write(byte b[], int off, int len)b[] 배열에 있는 자료의 off 위치부터 len 개수만큼 자료를 출력합니다.
void flush()출력을 위해 잠시 자료가 머무르는 출력 버퍼를 강제로 비워 자료를 출력합니다.
void close()출력 스트림과 연결된 대상 리소스를 닫습니다. 출력 버퍼가 비워집니다.
(예: FileOutputStream인 경우 파일 닫음)

FileOutputStreamTest.java

package stream.outputstream;

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

public class FileOutputStreamTest {

	public static void main(String[] args) {
		
//		try(FileOutputStream fos = new FileOutputStream("Output.txt"), true){ // default 값은 false로 true를 작성하면 파일 뒤에 붙여서 작성할 수 있음. false는 덮어쓰기
		try(FileOutputStream fos = new FileOutputStream("Output.txt")){
			fos.write(65);
			fos.write(66);
			fos.write(67);
		
		} catch(IOException e) {
			System.out.println(e);
		}
		System.out.println("end");

	}

}

실행 결과

end

Output.txt

ABC

FileOutputStreamTest.java

package stream.outputstream;

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

public class FileOutputStreamTest {

	public static void main(String[] args) {
		
		byte[] bs = new byte[26];
		byte data = 65;
		for(int i = 0; i < bs.length; i++) {
			bs[i] = data++;
		}
		
		try(FileOutputStream fos = new FileOutputStream("Output.txt")){
			fos.write(bs);
		
		} catch(IOException e) {
			System.out.println(e);
		}
		System.out.println("end");

	}

}

실행 결과

end

Output.txt

ABCDEFGHIJKLMNOPQRSTUVWXYZ

FileOutputStreamTest.java

package stream.outputstream;

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

public class FileOutputStreamTest {

	public static void main(String[] args) {
		
		byte[] bs = new byte[26];
		byte data = 65;
		for(int i = 0; i < bs.length; i++) {
			bs[i] = data++;
		}
		
		try(FileOutputStream fos = new FileOutputStream("Output.txt")){
			fos.write(bs, 2, 10);
		
		} catch(IOException e) {
			System.out.println(e);
		}
		System.out.println("end");

	}

}

실행 결과

end

Output.txt

CDEFGHIJKL // C부터 10개만

✔ flush()와 close() 메서드

  • 출력 버퍼를 비울 때 flush()메서드를 사용
  • close() 메서드 내부에서 flush()가 호출되므로 close()메서드가 호출되면 출력버퍼가 비워짐

◼ 문자 단위 스트림

✔ 문자 단위 스트림 - Reader

  • 문자 단위로 읽는 최상위 스트림
  • 하위 클래스에서 상속받아 구현함
스트림 클래스설명
FileReader파일에서 문자 단위로 읽는 스트림 클래스입니다.
InputStreamReader바이트 단위로 읽은 자료를 문자로 변환해주는 보조 스트림 클래스입니다.
BufferedReader문자로 읽을 때 배열을 제공하여 한꺼번에 읽을 수 있는 기능을 제공해주는 보조 스트림입니다.
  • 주요 메서드
메서드설명
int read()파일로부터 한 문자를 읽습니다. 읽은 값을 반환합니다.
int read(char[] buf)파일로부터 buf 배열에 문자를 읽습니다.
int read(char[] buf, int off, int len)파일로부터 buf 배열의 off 위치에서부터 len 개수만큼 문자를 읽습니다.
void close()스트림과 연결된 파일 리소스를 닫습니다.

FileReaderTest.java

package stream.reader;

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

public class FileReaderTest {

	public static void main(String[] args) {

		try(FileReader fr = new FileReader("reader.txt")){
			int i;
			while( (i = fr.read()) != -1){
				System.out.print((char)i);
			}
		}catch (IOException e) {
			e.printStackTrace();
		}
	}
}

reader.txt

ABC

실행 결과

ABC

FileWriterTest.java

package stream.writer;

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

public class FileWriterTest {

	public static void main(String[] args) {

		try(FileWriter fw = new FileWriter("writer.txt")){
			fw.write("A");
			char[] cbuf = {'B', 'C', 'D'};
			fw.write(cbuf);
			fw.write("안녕하세요");
			fw.write(cbuf, 1, 2);
			fw.write("123");
		} catch(IOException e) {
			System.out.println(e);
		}
	}

}

writer.txt

ABCD안녕하세요CD123

◼ 보조 스트림

✔ 보조 스트림

  • 실제 읽고 쓰는 스트림이 아닌 보조적인 기능을 추가하는 스트림
  • 데코레이터 패턴
  • FilterInputStream과 FilterOutputStream이 보조스트림의 상위 클래스
  • 생성자의 매개 변수로 또 다른 스트림을 가짐
생성자설명
protected FilterInputStream(InputStream in)생성자의 매개변수로 inputStream을 받습니다.
public FilterOutputStream(OutputStream out)생성자의 매개변수로 OutputStream을 받습니다.

✔ InputStreamReader와 OutputStreamWriter

  • 바이트 단위로 읽거나 쓰는 자료를 문자로 변환해주는 보조 스트림
  • FileInputStream(바이트 스트림)으로 읽은 자료를 문자와 변환하는 예

InputStreamReaderTest.java

package stream.decorator;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

public class InputStreamReaderTest {

	public static void main(String[] args) {
		
//		try(FileInputStream fis = new FileInputStream("reader.txt")){ 깨져서 출력된다.
		// FileReader로 읽을 수도 있지만 파일이 아닌 System.in같은 바이트 요소들을 문자로 변환할 필요가 있을 때 보조스트림 사용한다.
		try(InputStreamReader irs = new InputStreamReader(new FileInputStream("reader.txt"))) {
			int i = 0;
			while((i = irs.read()) != -1) {
				System.out.print((char)i);
			}
		} catch(IOException e) {
			System.out.println(e);
		}

	}

}

reader.txt

안녕하세요~ 자바프로그래밍시간입니다.

실행 결과

안녕하세요~ 자바프로그래밍시간입니다.

✔ Buffered 스트림

  • 내부적으로 8192 바이트 배열을 가지고 읽거나 쓰기 기능을 제공하여 속도가 빨라짐
스트링 클래스설명
BufferedInputStream바이트 단위로 읽는 스트림에 버퍼링 기능을 제공합니다.
BufferedOutputStream바이트 단위로 출력하는 스트림에 버퍼링 기능을 제공합니다.
BufferedReader문자 단위로 읽는 스트림에 버퍼링 기능을 제공합니다.
BufferedWriter문자 단위로 출력하는 스트림에 버퍼링 기능을 제공합니다.

FileCopyTest.java

package stream.decorator;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileCopyTest {

	public static void main(String[] args) {

		long milliseconds = 0;
		int len = 0;
		
		//a.exe설치파일 b.exe이름으로 복사
		try(FileInputStream fis = new FileInputStream("a.exe");
				FileOutputStream fos = new FileOutputStream("b.exe")){
			
			milliseconds = System.currentTimeMillis();
			int i = 0;
			while((i = fis.read()) != -1) {
				fos.write(i);
				len++;
			}
			milliseconds = System.currentTimeMillis() - milliseconds;
		} catch(IOException e) {
			System.out.println(e);
		}
		System.out.println(len);
		System.out.println(milliseconds);
	}

}

실행 결과
너무 오래 걸린다.

51077864
303491

[Buffered 스트림 이용]

FileCopyTest.java

package stream.decorator;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileCopyTest {

	public static void main(String[] args) {

		long milliseconds = 0;
		int len = 0;
		
		//a.exe설치파일 b.exe이름으로 복사
		try(FileInputStream fis = new FileInputStream("a.exe");
				FileOutputStream fos = new FileOutputStream("b.exe");
				BufferedInputStream bis = new BufferedInputStream(fis);
				BufferedOutputStream bos = new BufferedOutputStream(fos)){
			
			milliseconds = System.currentTimeMillis();
			int i = 0;
			while((i = bis.read()) != -1) {
				bos.write(i);
				len++;
			}
			milliseconds = System.currentTimeMillis() - milliseconds;
		} catch(IOException e) {
			System.out.println(e);
		}
		System.out.println(len);
		System.out.println(milliseconds);
	}

}

실행 결과
1.4초로 빨라졌다

51077864
1487

DataInOutStreamTest.java

package stream.decorator;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class DataInOutStreamTest {

	public static void main(String[] args) {

		try(FileOutputStream fos = new FileOutputStream("data.txt");
				DataOutputStream dos = new DataOutputStream(fos)){

			dos.write(100);
			dos.writeInt(10);
			dos.writeChar('A');
			dos.writeUTF("Hello");
			
		} catch(IOException e) {
			System.out.println(e);
		}
		
		try(FileInputStream fis = new FileInputStream("data.txt");
				DataInputStream dis = new DataInputStream(fis)){
			
			System.out.println(dis.read());
			System.out.println(dis.readInt());
			System.out.println(dis.readChar());
			System.out.println(dis.readUTF());
			
		} catch(IOException e) {
			System.out.println(e);
		}
		
	}

}

data.txt
우리는 깨져서 보임

d
 A ? Hello

실행 결과
내부적으로 자료형에 맞게 저장함.

100
10
A
Hello

◼ 직렬화

✔ 직렬화(serialization)

  • 인스턴스의 상태를 그대로 저장하거나(serialization) 다시 복원하는(deserialization)방식
  • 파일에 쓰거나 네트웍으로 전송
  • ObjectInputStream과 ObjectOutputStream 사용
생성자설명
ObjectInputStream(InputStream in)InputStream을 생성자의 매개변수로 받아 ObjectInputStream을 생성합니다.
ObjectOutputStream(OutputStream out)OutputStream을 생성자의 매개변수로 받아 ObjectOutputStream을 생성합니다.

✔ Serializable 인터페이스

  • 직렬화는 인스턴스 내용이 외부로 유출되는 것이므로 프로그래머가 직렬과 의도를 표시해야 함
  • 구현코드가 없는 maker interface
class Person implements Serializable { // 직렬화하겠다는 의도를 표시
	...
    String name;
    String job;
    ...
}

SerializationTest.java

package stream.serialization;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

class Person implements Serializable{
	
	private static final long serialVersionUID = 1L; // 이클립스에서 자동으로 시리얼번호 만들어줌
	
	String name;
	String title;
//	transient String title; // transient 키워드: 직렬화 할 때 이 멤버변수는 저장하지 않음
	
	public Person() {}
	public Person(String name, String title) {
		this.name = name;
		this.title = title;
	}
	
	public String toString() {
		return name + ", " + title;
	}
	
}

public class SerializationTest {

	public static void main(String[] args) throws ClassNotFoundException {
		
		Person personLee = new Person("Lee", "Manager");
		
		try(FileOutputStream fos = new FileOutputStream("serial.dat");
				ObjectOutputStream oos = new ObjectOutputStream(fos)){
			
			oos.writeObject(personLee);
			
		} catch(IOException e) {
			System.out.println(e);
		}
		
		try(FileInputStream fis = new FileInputStream("serial.dat");
				ObjectInputStream ois = new ObjectInputStream(fis)){
			
			Object obj = ois.readObject();
			if(obj instanceof Person) {
				Person p = (Person)obj;
				System.out.println(p);
			}
			
		} catch(IOException e) {
			System.out.println(e);
		}
		
	}

}

실행 결과

Lee, Manager

cf) Externalizable 인터페이스
프로그래머가 자료를 읽고 쓰는 방식을 직접 구현함

class Person implements Externalizable{
	...	
	//직접 변수를 통해 어떻게 write 할 건지 정할 수 있음
	//마찬가지로 쓰는 순서와 읽는 순서가 동일해야 함
	@Override
	public void writeExternal(ObjectOutput out) throws IOException {
		
	}
	
	@Override
	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
	}
	...
}

◼ 그 외 입출력 클래스

✔ 그 외 입출력 클래스

  • File 클래스
    파일개념을 추상화한 클래스
    입출력 기능은 없고 파일의 속성, 경로, 이름 등을 알 수 있음

  • RandomAccessFile 클래스
    입출력 클래스 중 유일하게 파일 입출력을 동시에 할 수 있는 클래스
    파일 포인터가 있어서 읽고 쓰는 위치의 이동이 가능함
    다양한 자료형에 대한 메서드가 제공됨

  • RandomAccessFile 예제

RandomAccessFileTest.java

package stream.others;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;

public class RandomAccessFileTest {

	public static void main(String[] args) throws IOException {
		
		RandomAccessFile rf = new RandomAccessFile("random.txt", "rw");
		
		rf.writeInt(100); // 4byte
		System.out.println(rf.getFilePointer()); // 파일 포인터 위치 출력
		
		rf.writeDouble(3.14); // 8byte
		System.out.println(rf.getFilePointer());
		
		rf.writeUTF("안녕하세요"); // 한글 하나당 3byte * 5 + null 2byte = 17byte
		System.out.println(rf.getFilePointer());
		
		rf.seek(0); // 파일 포인터 맨 앞으로 이동
		
		int i = rf.readInt();
		double d = rf.readDouble();
		String str = rf.readUTF();
		
		System.out.println(i);
		System.out.println(d);
		System.out.println(str);
	}

}

실행 결과

4
12
29
100
3.14
안녕하세요

0개의 댓글

관련 채용 정보