JAVA_입출력 스트림 (I/O Stream)

JW__1.7·2022년 8월 15일
0

JAVA 공부일지

목록 보기
25/30

데이터 입출력

데이터는 사용자로부터 키보드, 마우스 등을 통해 입력될 수 있고, 파일 또는 네트워크를 통해 입력될 수 있다.

스트림 (Stream)

프로그램은 외부에서 데이터를 읽거나 외부로 데이터를 출력하는 작업이 빈번하게 일어난다.
이때 데이터는 어떠한 통로를 통해서 데이터가 이동하는데, 이 통로를 Stream이라 한다.

자바에는 이러한 기능을 수행하기 위해 InputStreamOutputStream이 존재하며, 단일 방향으로 연속적으로 흘러간다.
InputStreamOutputStream은 추상 클래스이며, 추상 메소드를 오버라이딩해서 다양한 역할을 수행할 수 있다.
ex. 파일, 네트워크, 메모리 입출력

  • InputStream : 외부에서 데이터를 읽는 역할 수행
  • OutputStream : 외부로 데이터를 출력하는 역할 수행

java.io 패키지

  • 자바에서 기본적으로 제공하는 I/O 기능은 java.io 패키지에서 제공된다.
  • 파일 시스템의 정보를 얻기 위한 File 클래스와 데이터를 입출력하기 위한 다양한 입출력 스트림 클래스를 제공한다.
주요 클래스설명
File파일 시스템의 파일 정보를 얻기 위한 클래스
Console콘솔로부터 문자를 입출력하기 위한 클래스
InputStream / OutputStream바이트 단위 입출력을 위한 최상위 입출력 스트림 클래스
FileInputStream / FileOutputStream
DataInputStream / DataOutputStream
ObjectInputStream / ObjectOutputStream
PrintStream
BufferedInputStream / BufferedOutputStream
바이트 단위 입출력을 위한 하위 스트림
Reader / Writer문자 단위 입출력을 위한 최상위 입출력 스트림 클래스
FileReader / FileWriter
InputStreamReader / OutputStreamWriter
PrinterWriter
BufferedReader / BufferedWriter
문자 단위 입출력을 위한 하위 스트림 클래스
  • 바이트 단위 입출력 스트림
    : 그림, 멀티미디어, 문자 등 모든 종류의 데이터들을 주고 받을 수 있다.
  • 문자 단위 입출력 스트림
    : 오직 문자만 주고 받을 수 있다.

출력 스트림 (OutputStream)

  • 바이트 기반 출력 스트림의 최상위 추상클래스
  • 모든 바이트 기반 출력 스트림 클래스는 이 클래스를 상속 받는다.
메소드설명
void close( )OutputStream을 닫음
void flush( )버퍼에 남아있는 모든 데이터를 출력
void write(byte[ ] b)버퍼의 내용을 출력
void write(byte[ ] b, int off, int len)b배열 안에 있는 시작 off부터 len만큼 출력
abstract void write(int b)정수 b의 하위 1바이트를 출력

입력 스트림 (InputStream)

  • 바이트 기반 입력 스트림의 최상위 추상클래스
    • 모든 바이트 기반 입력 스트림은 이 클래스를 상속 받는다.
  • 파일 데이터를 읽거나 네트워크 소켓을 통해 데이터를 읽거나 키보드에서 입력한 데이터를 읽을 때 사용한다.
  • InputStream은 읽기에 대한 다양한 추상메소드를 정의
  • InputStream의 추상메소드를 오버라이딩하여 목적에 따라 데이터를 입력 받을 수 있다.
메소드설명
int available( )현재 읽을 수 있는 바이트 수를 반환
void close( )현재 열려있는 InputStream을 닫음
void mark(int readlimit)InputStream에서 현재의 위치를 표시
boolean markSupported( )해당 InputStream에서 mark( )로 지정된 지점이 있는지에 대한 여부를 확인
abstract int read( )InputStream에서 한 바이트를 읽어서 int값으로 반환
int read(byte[ ] b)byte[ ] b 만큼의 데이터를 읽어서 b에 저장하고 읽은 바이트 수를 반환
int read(byte[ ] b, int off, int len)len만큼 읽어서 byte[ ] b의 off 위치에 저장하고 읽은 바이트 수를 반환
void reset( )mark( )를 마지막으로 호출한 위치로 이동
long skip(long n)InputStream에서 n바이트만큼 데이터를 건너띄고 바이트 수를 반환

예제

1. 키보드로 입력한 데이터 읽기

java.util.Scanner 보다 성능이 좋다.

try {
	// System.in : 키보드와 연결된 바이트 스트림
	BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
			
	System.out.print("입력 >>> ");
	String str = br.readLine();
			
	System.out.println(str);
			
	br.close();
	} catch (IOException e) {
		e.printStackTrace();
	}

2. FileInputStream을 사용해서 storage 파일에서 b1.bin 생성하기

입력 데이터 단위

  • 1개 : int
  • 여러개 : byte[ ]

int read(byte[] b)

  • 읽은 내용은 byte 배열 b에 저장
  • 읽은 바이트 수를 반환
  • 읽은 내용이 없으면 -1 반환
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

public class Main {

	public static void m1() {
    
    	File file = new File("C:\\storage", "b1.bin");
		FileOutputStream fos = null;
        FileInputStream fis = null;
		
		try {
			// C:\\storage\\b1.bin 파일과 연결되는 바이트 출력 스트림
			fos = new FileOutputStream(file);
			
			// 출력할 데이터 선언
			int c = 'A';
			String str = "pple Mango 맛있다.";
			byte[] b = str.getBytes(StandardCharsets.UTF_8);	// str.getBytes("UTF-8")와 동일
			
			// 출력 (파일에 정보 생성)
			fos.write(c);
			fos.write(b);
		
        	// 여기까지 하면 C:\\storage 폴더의 b1.bin 파일에 "Apple Mango 맛있다." 메세지가 생성 된다.
        
		try {
			// 바이트 입력 스트림 생성
			fis = new FileInputStream(file);
			
			// 모든 정보를 StringBuilder에 저장한 뒤 확인
			StringBuilder sb = new StringBuilder();
			byte[] b = new byte[5];	// 5바이트씩 읽기 위해 준비
			int readByte = 0;
			
            // 읽어오기
			while((readByte = fis.read(b)) != -1) {		// readByte로 b(5개씩 읽기)를 해서 저장
				sb.append(new String(b, 0, readByte));	// String 객체를 생성하면서 offset과 length지정 String으로 가능
			}
			// 문자를 byteStream으로 읽었기 때문에 문제가 발생
			System.out.println(sb.toString());
			
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if(fis != null) fis.close();
                if(fos != null) fos.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}    
Apple Mango 맛있��.

한글이 깨져서 출력되는게 정상이다.
이것을 해결하기 위해서는 문자로 바꿔주는 InputStreamReader를 사용하면 된다.

3. InputStreamReader / OutputStreamReader를 이용해서 생성한 파일 안에 있는 한글이 깨지지 않게 출력하기

  • OutputStreamWriter : 문자 스트림을 바이트 스트림으로 변환
  • InputStreamReader : 바이트 입력 스트림을 문자 입력 스트림으로 변환

파일 생성

  • 출력 속도 향상을 위한 BufferedOutputStream , BufferedInputStream
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class Main {

	public static void m2() {
		
		// 출력 속도 향상을 위한 BufferedOutputStream
		File file = new File("C:\\storage", "b2.bin");
		FileOutputStream fos = null;
		BufferedOutputStream bos = null;
		
		try {
        	// 파일 생성
			fos = new FileOutputStream(file);
			bos = new BufferedOutputStream(fos);
			
			String str = "안녕하세요 반갑습니다.";
			byte[] b = str.getBytes("UTF-8");
			
			bos.write(b);
			
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if(bos != null) bos.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
        
	}
}    

파일 읽기

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

public class Main {

	public static void m2() {
		
		// 바이트 입력 스트림을 문자 입력 스트림으로 변환하는 InputStreamReader
		File file = new File("C:\\storage", "b2.bin");
		FileInputStream fis = null;
		InputStreamReader isr = null;
		
		try {
			fis = new FileInputStream(file);
			isr = new InputStreamReader(fis);
			
			StringBuilder sb = new StringBuilder();
			char[] cbuf = new char[5];	// 5글자씩 읽기 위해
			int readCnt = 0;
			
			while((readCnt = isr.read(cbuf)) != -1) {
				sb.append(cbuf, 0, readCnt);
			}
			System.out.println(sb.toString());
			
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if(isr != null) isr.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}    
안녕하세요 반갑습니다.

4. 변수를 그대로 출력해주는 DataOutputStream / DataInputStream을 이용해서 생성한 파일 읽어오기

  • DataOutputStream : 변수를 그대로 출력한다.
  • DataInputStream : 변수를 그대로 입력 받는다.

파일 생성

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class Main {

	public static void m3() {
		
		// 변수를 그대로 출력하는 DataOutputStream
		File file = new File("C:\\storage", "b3.dat");
		FileOutputStream fos = null;
		DataOutputStream dos = null;
		
		try {
			fos = new FileOutputStream(file);
			dos = new DataOutputStream(fos);
			
			// 출력할 변수
			String name = "에밀리";
			int age = 30;
			double height = 165.5;
			
			// 출력
			dos.writeUTF(name);
			dos.writeInt(age);
			dos.writeDouble(height);
			
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if(dos != null) dos.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
        
	}
}    

파일 읽기

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

public class Main {

	public static void m3() {
		
		// 변수를 그대로 입력 받는 DataInputStream
		File file = new File("C:\\storage", "b3.dat");
		FileInputStream	fis = null;
		DataInputStream dis = null;
		
		try {
			fis = new FileInputStream(file);
			dis = new DataInputStream(fis);
			
			String name = dis.readUTF();
			int age = dis.readInt();
			double height = dis.readDouble();
			
			System.out.println(name + ", " + age + ", " + height);	
		
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if(dis != null) dis.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}    
에밀리, 30, 165.5

5. 객체를 그대로 출력하는 ObjectOutputStream을 이용하여 ArrayList에 담긴 요소 출력하기

  • ObjectOutputStream : 객체를 그대로 출력한다.
  • ObjectInputStream : 객체를 그대로 입력 받는다.

파일 생성

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Arrays;
import java.util.List;

public class Main {

	public static void m4() {
		
		// 객체를 그대로 출력하는 ObjectOutputStream (보조스트림)
		File file = new File("C:\\storage", "b4.dat");
		FileOutputStream fos = null;
		ObjectOutputStream oos = null;
		
		try {
			// 1. User를 3개 저장한 ArrayList
			List<User> users = Arrays.asList(
					new User(1, "kim", 30),
					new User(2, "lee", 40),
					new User(3, "choi", 50)
			);
			// 2. User 1개
			User user = new User(4, "min", 60);
			
			fos = new FileOutputStream(file);
			oos = new ObjectOutputStream(fos);
			
			oos.writeObject(users);
			oos.writeObject(user);
			
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if(oos != null) oos.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
        
	}
}    
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.List;

public class Main {

	public static void m4() {
		
		// 객체를 그대로 입력 받는 ObjectInputStream
		File file = new File("C:\\storage", "b4.dat");
		FileInputStream	fis = null;
		ObjectInputStream ois = null;
		
		try {
			fis = new FileInputStream(file);
			ois = new ObjectInputStream(fis);
			
			List<User> users = (List<User>)ois.readObject();
			User user = (User)ois.readObject();
			
			for(User u : users) {
				System.out.println(u);
			}
			System.out.println(user);
			
		} catch(ClassNotFoundException e) {		// readObject 예외
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if(ois != null) ois.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}
User [userNo=1, name=kim, age=30]
User [userNo=2, name=lee, age=40]
User [userNo=3, name=choi, age=50]
User [userNo=4, name=min, age=60]

직렬화 (Serializable)

  • 스트림을 이용해서 객체를 전송하려면 직렬화를 해야 한다.
  • 직렬화가 필요한 객체는 Serializable 인터페이스를 구현해야 한다.
  • Serializable 인터페이스를 구현한 클래스는 SerialVersionUID 필드가 필요하다.
import java.io.Serializable;

public class User implements Serializable {
	
	private static final long serialVersionUID = -1830845902387248224L;
	private int userNo;
	private String name;
	private int age;
	
	public User(int userNo, String name, int age) {
		super();
		this.userNo = userNo;
		this.name = name;
		this.age = age;
	}

	@Override
	public String toString() {
		return "User [userNo=" + userNo + ", name=" + name + ", age=" + age + "]";
	}
}

0개의 댓글