자바 입출력과 스트림(Stream)

ednadev·2020년 7월 16일
0

자바

목록 보기
21/22

I/O란 Input과 Output의 약자로 입력과 출력, 간단히 줄여서 입출력이라고 한다. 입출력은 컴퓨터 내부 또는 외부의 장치와 프로그램 간의 데이터를 주고받는 것을 말한다.

자바에서 모든 입출력은 스트림(stream)을 통해 이루어진다.

데이터를 운반하는데 사용되는 연결통로이다. 스트림이란 자료(data) 흐름이 물의 흐름과 같다는 의미에서 사용되었다. 물이 한쪽 방향으로만 흐르는 것과 같이 스트림은 단방향 통신만 가능하기 때문에 하나의 스트림으로 입력과 출력을 동시에 처리할 수 없다.

입력과 출력을 동시에 수행하려면 입력을 위한 입력 스트림(input stream)과 출력을 위한 출력 스트림(output stream), 모두 2개의 스트림이 필요하다.

스트림은 먼저 보낸 데이터를 먼저 받게 되어 있으며 연속적으로 데이터를 주고받는다. 큐(queue)와 같은 FIFO(First In First Out) 구조로 되어 있다.

데이터 근원지를 Source, 데이터 종착점을 Sink, 연결한 것을 Stream
Source - 입력 스트림 - 출력 스트림 - Sink로 연결된다.

표준 입출력

그동안은 키보드가 source, 콘솔이 sink

System 클래스의 표준 입출력 멤버

public class System {
    public static PrintStream out; //표준 출력 스트림
    public static InputStream in; //표준 입력 스트림
    public static PrintStream err; //표준 에러 스트림
}

입력 클래스

System.in
한 바이트씩 읽어 들인다. 한글과 같은 여러 바이트로 된 문자를 읽기 위해서는 InputStreamReader와 같은 보조 스트림을 사용해야 한다.

Scanner 클래스
java.util 패키지에 있는 입력 클래스. 문자뿐 아니라 정수, 실수 등 다양한 자료형을 읽을 수 있다.

생성자가 다양하여 여러 소스로부터 자료를 읽을 수 있다.

  • Scanner(File source) : 파일을 매개변수로 받아 Scanner를 생성
  • Scanner(InputStream source) : 바이트 스트림을 매개변수로 받아 Scanner를 생성
  • Scanner(String source) : String을 매개변수로 받아 Scanner를 생성
Scanner sc = new Scanner(System.in);

표준 입력으로부터 자료를 읽어 들이는 기능을 사용할 수 있다.

  • Scanner 클래스에서 제공하는 메서드
    boolean nextBoolean() : boolean 자료를 읽는다.
    byte nextByte() : 한 바이트 자료를 읽는다.
    short nextShort() : short 자료형을 읽는다.
    int nextInt() : int 자료형을 읽는다.
    long nextLong() : long 자료형을 읽는다.
    float nextFloat() : float 자료형을 읽는다.
    double nextDouble() : double 자료형을 읽는다.
    String nextLine() : 문자열 String을 읽는다.

입출력 스트림

| Stream | 입력 | 출력 |
| --- | --- | --- |
| 문자 기반 스트림 (Character 계열) | Reader | Writer |
| 바이트 기반 스트림 (Byte 계열) | InputStream | OutputStream |

문자 기반 스트림 : 16bit 데이터가 통과. 한글, 일본어, 중국어 등 안 깨짐. 내가 보내는 데이터가 문자 기반이면 character 계열 스트림 사용

바이트 기반 스트림 : 8bit 데이터 기반. 영어는 깨지지 않지만 한글은 깨진다. 그림, 동영상, 음악 파일 등 대부분 파일은 바이트 단위.

문자는 무조건 Character 계열 스트림! 이미지, 동영상 등은 Byte 계열 스트림으로 보낸다. 객체를 보내는 건 Object 스트림

기반 스트림과 보조 스트림

기반 스트림 : 대상에 직접 자료를 읽고 쓰는 기능의 스트림
보조 스트림 : 직접 읽고 쓰는 기능은 없고 추가적인 기능을 제공해 주는 스트림. 기반 스트림이나 다른 보조 스트림을 생성자의 매개변수로 포함

문자 기반 스트림

Reader

문자 단위로 읽는 최상위 스트림

하위 클래스

FileReader : 파일에서 문자 단위로 읽는 스트림 클래스
InputStreamReader : 바이트 단위로 읽은 자료를 문자로 변환해 주는 보조 스트림
BufferedReader : 문자로 읽을 때 배열을 제공하여 한꺼번에 읽을 수 있는 기능을 제공해 주는 보조 스트림

메서드

파일로부터 문자를 읽는 read와 스트림과 연결된 파일 리소스를 닫는 close

Writer

문자 단위로 쓰는 최상위 스트림

하위 클래스

FileWriter : 파일에 문자 단위로 출력하는 스트림 클래스
OutputStreamWriter : 파일에 바이트 단위로 출력한 자료를 문자로 변환해 주는 보조 스트림
BufferedWriter : 문자로 쓸 때 배열을 제공하여 한꺼번에 쓸 수 있는 기능을 제공해 주는 보조 스트림

메서드

문자 내용을 출력하는 write와 출력하기 전에 출력 버퍼를 비우고 출력하는 flush, 파일과 연결된 스트림을 닫는 close

키보드로 데이터를 읽어들여서 콘솔로 출력하는 로직을 작성

  • Source : 키보드
  • Sink : 콘솔
  1. 스트림 생성 (자원을 열어서 쓰는 것) : 스트림 2개, character 계열
    입력 : InputStreamReader, BufferedReader

  2. 데이터를 읽어들인다 : InputStreamReader(read), BufferedReader(readLine)
    BufferedReader를 매핑하는 이유 : 겹쳐지는(감쳐진) 공간을 Buffered라고 한다. Buffered 공간으로 인해 BufferedReader에서 한 줄씩 읽을 수 있는 readLine function이 생겼다.

  3. 콘솔로 출력 : System.out.println

public class KeyboardInputTest {
    public static void main(String[] args) {
	// 1. 스트림 생성 : 입력스트림만 필요...InputStreamReader, BufferedReader
	System.out.println("1. Stream 생성.....");
	InputStreamReader ir = new InputStreamReader(System.in);
	BufferedReader br = new BufferedReader(ir);
		
	// 2. 읽어들인다...readLine()
	try {
	    System.out.println("2. readLine() 으로 읽어드립니다.....");
	    String line = br.readLine();
	    while(line!=null) { //읽어들인 데이터가 있다면...
		//3. 콘솔로 출력...System.out.println()
		System.out.println("Reading Data : " + line);
		line=br.readLine(); //이 부분이 있어야 반복한다.
	    }
	} catch (IOException e) {
		
	} finally {
	    try {
		br.close();
	    } catch (IOException e) {
	    }
	}
    }
}
public class KeyboardInputTest {
    public static void main(String[] args) throws IOException {
	// 1. 스트림 생성 : 입력스트림만 필요...InputStreamReader, BufferedReader
	System.out.println("1. Stream 생성.....");
	BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		
	// 2. 읽어들인다...readLine()
	try {
	    System.out.println("2. readLine() 으로 읽어드립니다.....");
	    String line = "";
	    while((line=br.readLine())!=null) { //읽어들인 데이터가 있다면...
                if(line.equals("종료")) {
                    System.out.println("입력금지...종료합니다.");
		    break;
                } else {
                    //3. 콘솔로 출력...System.out.println()
		    System.out.println("Reading Data : " + line);
                }
	    }
	} finally {
	    br.close();
	}
    }
}

파일에 있는 내용을 읽어서 콘솔로 출력하는 로직을 작성

  • Source : 파일
  • Sink : 콘솔
public class FileReadingTest {
    public static void main(String[] args) throws IOException {
	BufferedReader br = new BufferedReader(new FileReader("input.txt"));
	try {
	    String line = "";
	    while((line = br.readLine())!=null) {
		System.out.println(line);
	    }
	} finally {
	    br.close();
	}
    }
}

파일에 있는 내용을 읽어서 또 다른 파일로 출력하는 로직을 작성

  • Source : 파일
  • Sink : 파일... output.txt
public class FileReadingTest {
    public static void main(String[] args) throws IOException {
	BufferedReader br = new BufferedReader(new FileReader("input.txt"));
	BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"));

	try {
	    String line = "";
	    while((line = br.readLine())!=null) {
		bw.write(line);
		bw.newLine();
	    }
            // 파일 출력이 바로바로 되는 것을 확인..잘 쓰지 않는다
	    //bw.flush(); 
	} finally {
	    br.close();
	    bw.close();
	}
    }
}

  • true일 경우, append는 이어 쓰기, autoFlush는 덮어쓰기이다. autoFlush는 무조건 true!
public class FileReadingTest {
    public static void main(String[] args) throws IOException {
	BufferedReader br = new BufferedReader(new FileReader("input.txt"));
	PrintWriter pw = new PrintWriter(new FileWriter("output.txt", true), true);

	try {
	    String line = "";
	    while((line = br.readLine())!=null) {
		pw.println(line);
	    }
	} finally {
	    br.close();
	}	
    }
}
new FileWriter(String fileName, boolean append);
new PrintWriter(Writer out, boolean autoFlush);

바이트 기반 스트림

InputStream

바이트 단위 입력 스트림 최상위 클래스

하위 클래스

FileInputStream : 파일에서 바이트 단위로 자료를 읽는다
ByteArrayInputStream : Byte 배열 메모리에서 바이트 단위로 자료를 읽는다
FilterInputStream : 기반 스트림에서 자료를 읽을 때 추가 기능을 제공하는 보조 스트림의 상위 클래스

메서드

입력 스트림으로부터 자료를 읽는 read와 스트림과 입력 스트림과 연결된 대상 리소스를 닫는 close

OutputStream

바이트 단위 출력 스트림 최상위 클래스

하위 클래스

FileOutputStream : 바이트 단위로 파일에 자료를 쓴다
ByteArrayOutputStream : Byte 배열에 바이트 단위로 자료를 쓴다
FilterOutputStream : 기반 스트림에서 자료를 쓸 때 추가 기능을 제공하는 보조 스트림의 상위 클래스

메서드

자료를 출력하는 write와 출력 버퍼를 강제로 비워 자료를 출력하는 flush, 출력 스트림과 연결된 대상 리소스를 닫는 close

바이너리 데이터(이미지 파일)를 읽어서 파일로 출력하는 로직을 작성

  • 입력(2) : DataInputStream(FileInputStream)
  • 출력(2) : DataOutputStream(FileOutputStream)
public class DataInputStreamTest {
    public static void main(String[] args) {
	String fileName = "love.jpg";
	String outfileName = "forever.jpg";
		
	DataInputStream dis = null;
	DataOutputStream dos = null;
		
	try {
	    dis = new DataInputStream(new FileInputStream(fileName));
	    dos = new DataOutputStream(new FileOutputStream(outfileName));
			
	    int data = 0;
	    while((data = dis.readInt()) != -1) {
		dos.writeInt(data);
	    }
	} catch(EOFException e) { //주의
			
	} catch(IOException e) {
			
	}
    }
}

보조 스트림 Object Stream

직렬화(Serialization, unpack)

객체의 필드값이 직렬화돼서 객체 스트림을 통과하는 것

Serializable 인터페이스 (Data UnPack)

class Car implements Serializable {
    ...
}

transient 예약어 : 직렬화 하고 싶지 않은 변수를 설정할 때 사용

transient int price;

객체를 Sink 쪽으로 날린다.

  1. ObjectOutputStream 생성
  2. writeObject(object)

역 직렬화 (deserialization, pack)

반대로 스트림으로부터 데이터를 읽어서 객체를 만드는 것

  1. ObjectInputStream 생성
  2. readObject()

VO

직렬화 Serialization : Data UnPack

// 객체스트림을 직렬화로 통과할려면 반드시 implements Serializable
public class Car implements Serializable{ 
    private String model;
    private int price;

    public Car() {}
    public Car(String model, int price) {
	this.model = model;
	this.price = price;
    }
	
    public String getModel() {
	return model;
    }
    public void setModel(String model) {
	this.model = model;
    }
    public int getPrice() {
	return price;
    }
    public void setPrice(int price) {
	this.price = price;
    }
	
    @Override
    public String toString() {
	return "Car [model=" + model + ", price=" + price + "]";
    }
}

Service

직렬화, 역 직렬화 기능 모두 다 담는다

VO 객체를 여러 개 던지고 여러 개 받을 수 있다.
하나의 넣어서 한꺼번에 처리한다.

public class CarService {
    // 직렬화 기능...
    public void outputCar(ArrayList<Car> list, String path) throws Exception {
	ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));
	oos.writeObject(list);
	oos.close();
    }
	
    // 역직렬화 기능..
    public ArrayList<Car> inputCar(String path) throws Exception{
	ArrayList<Car> list = null;
		
	ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));
	list = (ArrayList<Car>)ois.readObject();
		
	return list;
    }
}

TEST

public class CarServiceTest {
    public static void main(String[] args) throws Exception {
	String path = "list.obj";
		
	CarService service = new CarService();
		
	ArrayList<Car> list = new ArrayList<>();
	list.add(new Car("렉스턴", 3500));
	list.add(new Car("아반떼", 2500));
	list.add(new Car("제너시스", 5000));
		
	service.outputCar(list, path); //직렬화
	System.out.println("list.obj에 자동차의 정보를 출력했습니다...");
	System.out.println("================================");
		
	ArrayList<Car> returnCar = service.inputCar(path);
	System.out.println("역직렬화로 자동차 정보를 가져와서 출력합니다...");
	for(Car c: returnCar) {
	    System.out.println(c);
	}
    }
}
profile
블로그 이사 중

관심 있을 만한 포스트

0개의 댓글