I/O Stream
스트림과 I/O 스트림 차이?
스트림: 데이터를 어떤 원하는 형태로 걸러내고 가공
I/O 스트림: 데이터의 입출력.
다수의 문자열을 저장하고 있는 파일이 있다. 저장된 문자열을 꺼내서 컬랙션 인스턴스에 저장, 저장된 문자열 중에서 길이가 5 이상인 문자열만 출력.
파일에 저장된 문자열을 꺼내서, 컬랙션 인스턴스에 저장
I/O 스트림이 해결해야 하는 부분이다.
길이가 5 이상 문자열 출력
스트림 해결
I/O 스트림 모델 소개
입출력 대상이 달라지면, 코드상에서 입출력 받는 방법도 달라진다. 입출력 대상에 관계 없이 동일한 방법으로 입출력을 진행할 수 있도록, I/O 스트림 모델이라는 것 정의
I/O 모델과 스트림의 이해. 그리고 파일 대상의 입력 스트림 생성
입력 스트림: 데이터를 읽어 들이는 스트림 (InputStream)
출력 스트림: 데이터를 내보내는 스트림 (OutputStream)
IntStream in = new FileInputStream(“data. dat”) ;
FileInputStream은 InputStrem을 상속한다.
입력 스트림의 생성도 인스턴스의 생성을 통해서 이뤄진다.
public abstract int read() throws IOException
int data = in.read -> 데이터를 읽어드림
out.write(7); -> 데이터 7을 파일에 전달.
import java.io.*;
public class Main1 {
public static void main(String[] args) throws IOException {
OutputStream out = new FileOutputStream("data.dat");
out.write(7);
out.close();
InputStream in = new FileInputStream("data.dat");
int dat = in.read();
in.close();
System.out.println(dat);
}
}
OutputStream out = new FileOutputStream("data.dat");
FileOutStream 클래스는 OutputStream을 상속한다.
만약 스트림을 생성할 대상이 파일이 아니라며, 인스턴스 종류 달라진다. but 스트림 생성하고 나면, 그 대상에 상관없이 다음의 방법으로 데이터를 저장할 수 있따.
out.write(7) = 실제 파일로 데이터를 저장하도록 구현된 wirte 메소드는 FileOutputStream 정의. 생성했던 출력 스트림을 소멸해야 한다. 스트림이 소멸되면, 열려있던 파일은 닫히고 할당되었던 메모리 자원은 다시 활용 할 수 있도록 반호나
out.close () -> 출력 스트림의 종료 및 소멸
InputStream in = new FileInputStream("data.dat");
int dat = in.read();
in.close();
System.out.println(dat);
}
입출력 스트림 관련 코드의 개선
입출력 스트림 생성 과정에서 예외는 발생하며, 스트림 생성에 실패할 수도 있다. 그런데 이러한 경우에는 스트림을 종료하는 close 메소드의 호출을 생략해야 한다. 그렇지 않으면 이로 인해 또다른 예외가 발생할 수도 있다.
import jdk.internal.util.xml.impl.Input;
import java.io.*;
public class Main2 {
public static void main(String[] args) throws IOException {
OutputStream out = null;
try {
out = new FileOutputStream("data.da");
out.write(7);
}
finally {
if (out != null){
out.close();
}
}
InputStream in = null;
try {
in = new FileInputStream("data.dat");
int data = in.read();
System.out.println(data);
}
finally {
if (in !=null){
in.close();
}
}
}
}
import java.io.*;
public class Main3 {
public static void main(String[] args) throws IOException {
try(OutputStream out = new FileOutputStream("data.dat")){
out.write(7);
} catch (IOException e){
e.printStackTrace();
}
try(InputStream in = new FileInputStream("data.dat")){
int dat = in.read();
System.out.println(dat);
} catch (IOException e){
e.printStackTrace();
}
}
}
안정적 close 메소드의 호출을 보장하기 위한 코드가 추가. try – with – resource 문을 적용하면 간결한 구조가 되게 할 수 있다.
바이트 단위 입출 및 출력 스트림
바이트 단위로 데이터를 입력 및 출력하는 스트림을 가리켜 바이트 스트림이라고 한다.
import java.io.*;
import java.util.Scanner;
public class Main4 {
public static void main(String[] args) throws IOException {
Scanner sc = new Scanner(System.in);
System.out.println("대상파일");
String s = sc.nextLine();
System.out.println("사본 이름 :" );
String dst = sc.nextLine();
try(InputStream in = new FileInputStream(s);
OutputStream out = new FileOutputStream(dst)) {
int data;
while (true){
data = in.read();
if (data == - 1)
break;
out.write(data);
}
}catch (IOException e){
e.printStackTrace();
}
}
}
파일을 복사하기 위해서는 입력 스트림과 출력 스트림을 함께 써야 한다. 그리고 파일 복사를 실제 진행하는 코드는 위와 같다.
while (true){
data = in.read();
if (data == - 1)
break;
out.write(data);
}
while문에서 호출하는 read 메소드는 다음과 같다.
public int read() throws IOException
읽어드린 1바이트의 유효한 데이터에 3바이트의 0을 채워서 4바이트 int형 데이터로 도달. 반면에 스트림 끝에 도달해서 더 이상 읽어 들을 데이터가 없는 경우 -1을 반환. write 메소드는 read 메소드와 유사하게 인자로 전달되는 int형 데이터의 첫번째 바이트만을 파일에 저장.
보다 빠른 속도의 파일 복사 프로그램
바이트 스트림이라고 하여, 꼭 1바이트만 써야 하는 것은 아니다. byte 배열을 생성해서 이를 기반으로 많은 양의 데이터를 한번에 읽고 쓰는 것도 가능
import java.io.*;
import java.util.Scanner;
public class Main5 {
public static void main(String[] args) throws IOException {
Scanner sc = new Scanner(System.in);
System.out.println("File");
String src = sc.nextLine();
System.out.println("사본");
String dst = sc.nextLine();
try(InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dst)){
byte[] brf = new byte[1024];
int len;
while (true) {
len = in.read(brf); //buf로 읽고
if (len == -1)
break;
out.write(brf, 0, len); // len 바이트 만큼 데이터를 저장
}
}
catch (IOException e){
e.printStackTrace();
}
}
}
read는 읽어 들인 바이트의 수를 반환하는데 스트림 끝에 도달해서 더 이상 읽어드릴 데이터가 없는 경우 -1 반환
public int read (byte[] b) throws IOException
파일에 저장된 데이터를 b로 전달된 배열에 저장
public void writhe(byte[] b, int off, int len) throws IOException
b로 전달된 배열의 데이터를 인덱스 off에서부터 len 바이트만큼 파일에 저장
필터 스트림의 이해와 활용
읽어 들인 결과는 코드상에서 int형 데이터로 활용하지 못한다. int형 데이터 하나를 읽어 들이면, 다음 단계를 거쳐야 한다.
1 단계 : 파일로부터 1바이트 데이터 4개를 읽어드린다.
2 단계 : 읽어드린 1바이트 데이터 4개를 하나의 int형 데이터로 조합
두단계의 읽을 하는 스트림을 가리켜, 필터 스트림이라고 한다. 입력 또는 출력 스트림에 덧붙여서 데이터를 조합, 가공 및 분리 역할
필터 입력 스트림: 입력 스트림에 연결되는 필터 스트림
필터 출력 스트림: 출력 스트림에 연결되는 필터 스트림
int형 데이터 370을 읽기 위해서 스트림은 파일로부터 4개의 1바이트 데이터를 읽어서 필터 입력 스트림에 전달.
입력
InputStream in = new FileInputStream(“data.dat”) //입력 스트림 및 생성
DataInputStream fIn = new DataInputStream(in) //필터 스트림 생성 및 연결
저장
OutputStream out = new FileOutputStream(“data.dat”) // 출력 스트림 생성
DataOutputStream fOut = new DataOutputStream(out) // 필터 스트림 생성 및 연결
기본 자료형 데이터의 입출력에 필요한 필터 스트림은
DataInputStream, DataOutputStream 이다.
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Main6 {
public static void main(String[] args) throws IOException {
try(DataOutputStream out =
new DataOutputStream(new FileOutputStream("data.dat"))){
out.writeInt(370);
out.writeDouble(3.14);
} catch (IOException e){
e.printStackTrace();
}
}
}
DataOutputStream out =
new DataOutputStream(new FileOutputStream("data.dat"
파일 출력 스트림을 생성, 필터 스트림을 연결.
기본 자료형을 대상으로, 그 값을 저장할 수 있는 메소드들이 각각 있다.
writeInt, wirteDouble
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
public class Main7 {
public static void main(String[] args) throws IOException {
try(DataInputStream in =
new DataInputStream(new FileInputStream("data.dat"))){
int num1 = in.readInt();
double num2 = in.readDouble();
System.out.println(num1);
System.out.println(num2);
} catch (IOException e){
e.printStackTrace();
}
}
}
파일에 데이터 저장한 순서대로, 자료형을 일치시켜 꺼내고 있다.
자료형을 결정해서 데이터 입출력하는 경우, 순서를 지키는 것이 중요하다.
버퍼링 기능을 제공하는 필터 스트림
BufferedInputStream -> 버퍼링 기능을 제공하는 버퍼 입력 스트림
BufferedOutputStream -> 버퍼링 기능을 제공하는 버퍼 출력 스트림
import java.io.*;
import java.util.Scanner;
public class Main8 {
public static void main(String[] args) throws IOException {
Scanner sc = new Scanner(System.in);
System.out.println("File");
String src = sc.nextLine();
System.out.println("사본 이름");
String dxt = sc.nextLine();
try(BufferedInputStream in = new BufferedInputStream(new FileInputStream(src));
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(dxt))){
int data;
while (true){
data = in.read();
if (data == -1)
break;
out.write(data);
}
} catch (IOException e){
e.printStackTrace();
}
}
}
BufferedInputStream in = new BufferedInputStream(new FileInputStream(src)
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(dxt))
버퍼 스트림은 내부에 버퍼 공간을 갖는다. 그리고 입력 스트림으로부터 많은 양의 데이터를 가져다가 해당 버퍼를 채운다. read 메소드를 호출할 때 파일에 저장된 데이터를 반환하는 것이 아니라, 버퍼 스트림의 버퍼에 저장된 데이터를 반환한다. 파일에서 데이터를 읽을 때 속도가 저하되는 것은 아니다. 파일을 한번에 전송할 때 쓰면 속도 저하 없이 보낼 수 있다
버퍼링 기능에 flush
버퍼 스트림에 저장된 데이터가 파일에 저장되지 않은 상태에서 다운될 경우
write 메소드를 사용했지만, 데이터를 저장했음에도 불구하고 실제 파일에는 데이터가 저장되지 않은 일이 발생할 수 있다. 파일에 저장해야 할 중요한 데이터가 있다면, 메소드 호출을 명시적을 버퍼를 비우라고, 명령할 수 있다
public void flush() throws IOException
이 메소드를 빈번히 호출하면, 버피링을 통한 성능 향상에 방해가 되기 때문에 제한적으로 호출
파일에 기본 자료형 데이터를 저장, 버퍼링 기능 추가
BufferedOutputStream out = new BufferedOutputStream(out)
파일을 대상으로 버퍼링 기능을 갖는 스트림을 생성하여 기본 자료형 데이터를 저장. 버퍼링 기능의 필터 스트림과 기본 자료형 데이터의 저장을 동시에 필요
OutputStream out = new FileOutputStream(“data.dat”)
BufferedOutputStream bfOut = new BufferedOutpuStream(out);
DataOutputStream dfOut = new DataOutputStream(bfout)
버퍼 스트림은 기본 입출력 스트림에 연결되어 입출력 효율을 향상시키는 필터 스트림.
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
public class Main10 {
public static void main(String[] args) throws IOException {
try (DataInputStream in = new DataInputStream(
new BufferedInputStream(
new FileInputStream("data.dat")))) {
int num1 = in.readInt();
double num2 = in.readDouble();
System.out.println(num1);
System.out.println(num2);
} catch (IOException e){
e.printStackTrace();
}
}
}
저장한 값을 꺼내되, 버퍼링 기능도 존재하는 스트림 생성
문자 스트림의 이해와 활용
바이트 스트림과 문자 스트림 차이
데이터의 변화 없이 바이트 단위로 데이터를 입력 출력하는 것이 입출력의 기본. 그러나 문자를 입출력 할 때는 데이터 수정이 필요. 또한 문자 스트림이라는 것도 지원
“유니코드로 표현된 문자를 해당 운영체제의 문자 표현 방식으로 변경해서 저장”
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
public class Main11 {
public static void main(String[] args) throws IOException {
try(Writer out = new FileWriter("data.txt")) {
out.write('A');
out.write('한');
}
catch (Exception e){
e.printStackTrace();
}
}
}
문자 출력 스트림을 생성해서 문자를 저장
FileReader & FileWriter
바이트 스트림 생성 관련 클래스
InputStream , OutputStream
문자 스트림의 생성과 관련된 클래스가 상속
Reader, Writer
파일을 대상으로 하는 바이트 입출력 스트림을 생성하는 클래스
FileInputStream, FileOutputStream
파일을 대상으로 하는 문자 입출력 스트림 생성
FileReader , FileWriter
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.Scanner;
public class Main12 {
public static void main(String[] args) throws IOException {
Scanner sc = new Scanner(System.in);
System.out.println("읽을 파일");
String str = sc.nextLine();
try (Reader reader = new FileReader(str)) {
int ch;
while (true) {
ch = reader.read();
if (ch == -1)
break;
System.out.println((char) ch);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
ch = reader.read();
if (ch == -1)
break;
하나의 문자를 반환 , 반환 문자 없으면 -1 반환
read의 반환형은 int이다. int인 이유는 반환 할 문자가 없다면 -1 반환 하기 위함.
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
public class Main13 {
public static void main(String[] args) throws IOException {
try(Writer out = new FileWriter("data.txt")){
for (int ch = (int)'A'; ch < (int)('z' + 1) ; ch++) {
out.write(ch);
}
}
catch (IOException e){
e.printStackTrace();
}
}
}
for (int ch = (int)'A'; ch < (int)('z' + 1) ; ch++) {
out.write(ch);
}
A부터 Z까지 1씩 증가시키면서 저장하면 추력 스트림을 통해 알파벳 저장
BufferedReader & BufferedWriter
바이트 스트림에는 필터 스트림을 연결할 수 있었다.
BufferedInputStream -> 바이트 기반 버퍼 입력 스트림
BufferedOutputStream -> 바이트 기반 버퍼 출력 스트림
문자 스트림에도 필터 스트림을 연결할 수 있다. 문자 스트림에 버퍼링 기능을 제공하는 필터 스트림 둘은 같다.
BufferedReader / BufferedWriter
문자열 단위로 입력 및 출력을 진행할 수 있는 메소드가 정의
public String readLine() throws Exception -> BufferedReader의 메소드
문자열 반환, 반환할 문자열 없으면 null 출력
public void write (String s, int off, int len) throws Exception -> BufferedWriter 메소드
문자열 s를 인덱스 off에서 len개의 문자까지 저장
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class Main14 {
public static void main(String[] args) throws IOException {
String ks = "돈 필요";
String es = "인생은 길다";
try(BufferedWriter writer = new BufferedWriter(new FileWriter("String.txt"))){
writer.write(ks, 0 , ks.length());
writer.newLine();
writer.write(es, 0 , es.length());
}
catch (IOException e){
e.printStackTrace();
}
}
}
writer.newLine();
줄 바꿈 문자를 삽입
생성한 파일에 저장된 문자열을 출력하는 예제
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class Main15 {
public static void main(String[] args) throws IOException {
try(BufferedReader br = new BufferedReader(new FileReader("String.txt"))){
String str;
while (true){
str = br.readLine();
if (str == null)
break;
System.out.println(str);
}
}
catch (IOException e){
e.printStackTrace();
}
}
}
I/O 스트림 기반의 인스턴스 저장
인스턴스를 통째로 저장하는 것을 가리켜 객체 직렬화, 역으로 저장된 인스턴스를 꺼내는 것을 객체 역 직렬화
ObjectInputStream & ObjectOutputStream
인스턴스 저장은 당연히 바이트 스트림을 통해서 이뤄진다. 바이트 기반의 기본 입출력 스트림은 다음 스트림을 연결하면 인스턴스를 통째로 입력하고 출력할 수 있다.
ObjectInputStream -> 인스턴스를 입력하는 스트림
ObjectOutputStream -> 인스턴스를 출력하는 스트림
이들은 필터 스트림으로 구분하지 않는다. 그 이유는 필터 스트림이 상속하는 두 클래스를 상속하지 않기 때문이다.
FilterOutputStream, FilterInputStream
“입출력의 대상이 되는 인스턴스의 클래스는 Serializable을 구현해야 한다.
직렬화 기능함을 표시
import java.io.*;
class SBox implements Serializable {
String s;
public SBox(String s){
this.s = s;
}
public String get(){
return s;
}
}
public class Main16 {
public static void main(String[] args) {
SBox sBox = new SBox("Robot");
SBox sBox1 = new SBox("Strawberry");
try(ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream("Object.bin"))){
oo.writeObject(sBox);
oo.writeObject(sBox1);
}
catch (IOException e){
e.printStackTrace();
}
}
}
위 예제를 실행하면 Object.bin 이라는 파일이 생성되고, 두개의 SBox 인스턴스가 저장된다.
ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream("Object.bin"))){
oo.writeObject(sBox);
ObjectOutputStream에는 다양한 writeXXX 메소드가 정의되어 있어서 문자열 및 기본 자료형 데이터도 하나의 파일에 담을 수 있다.
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class Main17 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
try(ObjectInputStream oi = new ObjectInputStream(new FileInputStream("object.bin"))){
SBox box1 = (SBox) oi.readObject();
System.out.println(box1.get());
SBox box2 = (SBox) oi.readObject();
System.out.println(box2.get() );
}
}
}
transient
class SBox implements Serializable {
String s;
public SBox(String s){
this.s = s;
}
public String get(){
return s;
}
}
“인스턴스를 저장하면 인스턴스 변수가 참조하는 인스턴스까지 함께 저장된다. “
s가 참조하는 인스턴스까지 함께 저장. 함께 저장되기 위해서는 인스턴스까지 Serializable을 구현하고 있어야 한다. 민약 참조변수 s가 참조하는 인스턴스의 저장을 원하지 않으면 transient을 선언을 추가하면 된다
transient String s;
참조변수가 참조하는 대상은 저장하지 않겠다는 선언
인스턴스 변수 앞에 transient 선언을 추가하면, 변수가 참조하는 인스턴스는 저장되지 않는다 복원시 참조변수는 null 초기화
transient 선언은 기본자료형 변수에도 적용가능