JAVA/HIGH JAVA

[JAVA] 입출력 ( I/O ) - Buffered 스트림, Object 스트림, serialization

아잠만_ 2024. 5. 7. 10:20

파일 열기 창, 저장창을 활용한 파일 복사 예제

  • 파일 열기/저장 창
    JFileChooser
  • 확장자 설정
    FileNameExtensionFilter
  • 파일 유형 추가
    [열기/저장창 객체].addChoosableFileFilter(확장자 객체) 
  • 파일 유형 기본값
    [열기/저장창 객체].setFileFilter(기본값으로 설정할 확장자 객체)
  • Dialog 창 기본경로
    [열기/저장창 객체].setCurrentDirectory(새로운 파일 객체(디렉토리) )
  • Dialog 열기
    [열기/저장창 객체].showOpenDialog(new Panel())
  • Dialog 저장
    [열기/저장창 객체].showSaveDialog(new Panel())
  • Dialog창이 열렸는지 확인 여부
    JFileChooser.APPROVE_OPTION

import java.awt.Panel;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import javax.swing.JFileChooser;
import javax.swing.filechooser.FileNameExtensionFilter;

public class FileCopyDialog {
	public static void main(String[] args) {
		new FileCopyDialog().fileCopyStart();
	}

	// 시작 메서드
	public void fileCopyStart() {
		// File sourceFile = new File("d:/d_other/코알라.jpg")
		File sourceFile = showDialog("OPEN");
		
		// 취소 눌렀을 때
		if(sourceFile==null) {
			System.out.println("복사할 원본 파일이 선택되지 않았습니다");
			System.out.println("복사 작업 중지..");
			return;
		}

		if (!sourceFile.exists()) {
			System.out.println(sourceFile.getPath());
			System.out.println("복사 작업을 마칩니다..");
			return;
		}

		File targetFile = showDialog("SAVE");
		// 취소 눌렀을 때
		if(targetFile==null) {
			System.out.println("대상 파일이 선택되지 않았습니다");
			System.out.println("복사 작업 중지..");
			return;
		}

		// 원본 파일을 읽어올 스트림 객체 생성
		FileInputStream fi = null;

		// 대상 파일로 저장될 스트림 생성
		FileOutputStream fo = null;
		try {
			fi = new FileInputStream(sourceFile);

			fo = new FileOutputStream(targetFile);
			int c;
			while ((c = fi.read()) != -1) {
				fo.write(c);
			}
			System.out.println("복사 작업 완료..");

		} catch (IOException e) {
			// TODO: handle exception
		} finally {
			if (fi == null) {
				try {
					fi.close();
				} catch (IOException e2) {
					// TODO: handle exception
				}
			}
			if (fo == null) {
				try {
					fo.close();
				} catch (IOException e2) {
					// TODO: handle exception
				}
			}
		}
	}

	public File showDialog(String option) {
		// SWING의 파일 열기 창, 저장 창 연습

		JFileChooser chooser = new JFileChooser();

		// 선택할 파일 확장자 설정
		FileNameExtensionFilter txt = new FileNameExtensionFilter("텍스트문서(*.txt)", "txt");

		// 파일 확장자가 여러 개 일 경우 배열로 선언
		FileNameExtensionFilter img = new FileNameExtensionFilter("이미지 파일", new String[] { "jpg", "png", "gif" });

		// 가변 변수 처럼 나열해서 쓸 수도 있음
		FileNameExtensionFilter doc = new FileNameExtensionFilter("MS Word 파일", "docx", "doc");

		// '모든 파일' 목록 표시 여부 설정하기 (true일 때 출력)
		chooser.setAcceptAllFileFilterUsed(true);

		chooser.addChoosableFileFilter(txt);
		chooser.addChoosableFileFilter(img);
		chooser.addChoosableFileFilter(doc);

		// 확장자 목록 중 기본값으로 선택될 확장자 설정하기
		chooser.setFileFilter(txt);

		// Dialog 창이 나타낼 기본 경로 설정하기
		chooser.setCurrentDirectory(new File("d:/d_other"));

		// 창 보여주기
		int result;
		if ("OPEN".equals(option.toUpperCase())) {
			result = chooser.showOpenDialog(new Panel()); // 열기용
		} else if ("SAVE".equals(option.toUpperCase())) {
			result = chooser.showSaveDialog(new Panel()); // 저장용
		} else {
			System.out.println("option 매개변수에는 'OPEN'또는 'SAVE'만 사용하세요");
			return null;
		}

		File selectedFile = null;
		// 보여진 창에서 파일을 선택한 후 '열기' 또는 '저장' 버튼을 눌렀을 경우 처리하기
		if (result == JFileChooser.APPROVE_OPTION) { // 열기나 저장버튼 눌렀는 지 확인
			selectedFile = chooser.getSelectedFile();
			System.out.println("선택한 파일 : " + selectedFile.getAbsolutePath());
		}

		return selectedFile;
	}
}

Buffered 스트림

  • 입출력 효율을 높이기 위해 버퍼를 사용하는 보조 스트림
  • 버퍼의 기본 크기는 8KB( 8192 bytes )이다
  • 크기를 생성하지 않은 경우 기본 크기를 가진다

 

write()로 크기만 한 값을 출력하지만

 

출력작업이 끝나면 버퍼에 남아 있는 데이터를 모두 강제 출력 시켜야한다
이 때 flush()가 그런 역할을 수행한다.
close() 안에도 flush() 기능이 있기 때문에 모든 데이터를 출력시켜주지만 가급적 flush()를 사용해주는 것이 좋음
사이에 다른 작업을 이어서 해줄 때가 있으므로 그 때 출력 작업이 마무리되지 않은 상태로 작업하면 엉뚱하게 일어날 수 있기 때문에 바로  flush()를 사용하여 제대로 출력시켜주는 것이 좋다

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

public class BufferedIOTest01 {
	public static void main(String[] args) {
		// 입출력의 성능 향상을 위해서 Buffered스트림을 사용한다
		try {
			FileOutputStream fout = new FileOutputStream("d:/d_other/bufferTest.txt");
			
			// 버퍼의 크기가 5인 버퍼스트림 객체 생성
			BufferedOutputStream bout = new BufferedOutputStream(fout, 5); 
			
			// 크기가 5임으로 5개 출력
			for(int i='1'; i<='9'; i++) {
				bout.write(i);				// 12345
			}
			// 출력작업이 끝나면 버퍼에 남아 있는 데이터를 모두 강제 출력 시켜야한다
			// 이 때 flush()가 그런 역할을 수행한다.
			bout.flush();	// 123456789
			
			// close() 안에도 flush() 기능이 있기 때문에 모든 데이터를 출력시켜주지만 가급적 flush()를 사용해주는 것이 좋음
			// 사이에 다른 작업을 이어서 해줄 때가 있으므로 그 때 출력 작업이 마무리되지 않은 상태로 작업하면 엉뚱하게 일어날 수 있기 때문에
			// 바로  flush()를 사용하여 제대로 출력시켜주는 것이 좋다
			
			// 스트림 닫기
			// 보조 스트림을 닫으면 보조스트림에서 사용한 기반이 되는 스트림도 같이 닫힌다.
			bout.close();	// 123456789
			
			System.out.println("출력 작업 끝...");
			
		} catch (IOException e) {
			// TODO: handle exception
		}
	}
}

 


문자 기반 buffered 스트림

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

public class BufferedIOTest02 {
	public static void main(String[] args) {
		
		try {
			// 문자 파일읽기
			FileReader fr = new FileReader("./src/kr/or/ddit/basic/FileTest01.java"); 
			
			BufferedReader br = new BufferedReader(fr);	// 버퍼의 크기를 지정하지 않으면 8kb
			
			String temp = "";
			
			// readLine 한줄 씩 읽어옴
			// 더이상 읽어올 데이터가 없을 때 null반환
			for(int i=1; (temp = br.readLine()) != null; i++) {
				System.out.printf("%4d :  %s\n", i, temp);
			}
			br.close(); // 스트림 닫기
			
		} catch (IOException e) {
			// TODO: handle exception
		}
	}
}

데이터 저장 / 읽어오기

DataInputStream

DataOutputStream


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

public class DataIOTest {
	public static void main(String[] args) {
		try {
			FileOutputStream fout = new FileOutputStream("d:/d_other/dataTest.dat");
			
			// 기본 자료형 단위로 출력할 보조 스트림 객체 생성
			DataOutputStream dout = new DataOutputStream(fout);
			
			dout.writeInt(200);	// 정수형으로 출력
			dout.writeFloat(123.45f);	// 실수형(float)으로 출력
			dout.writeBoolean(false);	// 논리형으로 출력
			dout.writeUTF("ABCDabcd");	// 문자열형식으로 출력
			
			System.out.println("출력 완료...");
			
			dout.close(); // 스트림 닫기
			
		} catch (IOException e) {
			// TODO: handle exception
		}
		
		System.out.println("============");
		
		// 출력한 자료 읽어오기
		try {
			FileInputStream fin = new FileInputStream("d:/d_other/dataTest.dat");
			DataInputStream din = new DataInputStream(fin);
			
			// 자료를 읽어와 화면에 출력하기
			
			// DataInputStream으로 자료를 읽어올 때는 출력할 때와 같은 순서로 읽어와야 한다
			System.out.println("정수형 : "+din.readInt());
			System.out.println("실수형 : "+din.readFloat());
			System.out.println("논리형 : "+din.readBoolean());
			System.out.println("문자열 : "+din.readUTF());
			
			System.out.println();
			System.out.println("읽기 작업 완료...");
			
			din.close(); // 스트림 닫기
		} catch (IOException e) {
			// TODO: handle exception
		}
	}
}

직렬화(serialization)

transient ==> 직렬화가 되지 않을 멤버 변수에 지정한다.

Object 객체를 저장하는 stream

ObjectOutputStream

ObjectInputStream


입력

import java.io.Serializable;

public class Member implements Serializable {
	private String name;
	private int age;
	private String addr;
	
	public Member(String name, int age, String addr) {
		super();
		this.name = name;
		this.age = age;
		this.addr = addr;
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public String getAddr() {
		return addr;
	}
	public void setAddr(String addr) {
		this.addr = addr;
	}
	
	
}
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class ObjectIOTest01 {
	public static void main(String[] args) {
		// Member의 인스턴스 생성
		Member mem1 = new Member("홍길동", 20, "대전");
		Member mem2 = new Member("홍길서", 20, "인천");
		Member mem3 = new Member("홍길남", 30, "부산");
		Member mem4 = new Member("홍길북", 40, "속초");
		
		// 객체를 파일로 저장하기
		try {
			// 출력용 스트림 객체 생성
			FileOutputStream fout = new FileOutputStream("d:/d_other/memObj.dat");
			BufferedOutputStream bout = new BufferedOutputStream(fout);
			ObjectOutputStream oout = new ObjectOutputStream(bout);
			
			// 쓰기 작업
			System.out.println("객체 저장 작업 시작..");
			oout.writeObject(mem1);
			oout.writeObject(mem2);
			oout.writeObject(mem3);
			oout.writeObject(mem4);
			
			// 객체를 저장할 때 마지막에 null값을 추가하여 EOFException을 방지할 수 있다.
			oout.writeObject(null);
			
			System.out.println("객체 저장 작업 완료..");
			
			oout.close();	// 스트림 닫기 (fout bout oout 스트림을 닫음)
			
			
			
		} catch (IOException e) {
			e.printStackTrace();
//			java.io.NotSerializableException
		}
	}
}

출력

import java.io.BufferedInputStream;
import java.io.EOFException;
import java.io.FileInputStream;
import java.io.ObjectInputStream;

public class ObjectIOTest02 {
	public static void main(String[] args) {
		// 파일에 저장된 객체를 읽어와 그 내용을 화면에 출력하기
		try {
			// 입력용 스트림 객체 생성
			ObjectInputStream oin = new ObjectInputStream(new BufferedInputStream(new FileInputStream("d:/d_other/memObj.dat")));
		
			Object obj = null; // 읽어온 객체를 저장할 변수
			
			System.out.println("객체 읽기 작업 시작..");
		
			
			// readObject() 메서드가 데이터를 끝까지 다 읽어오면  EOFException이 발생한다
			// 오류가 발생되어 반복문 다음 문장이 출력이 되지 않음
			// 발생되지 않기 위해 입력할 때 null값을 추가
			while((obj = oin.readObject())!=null) {
				// 읽어온 데이터는 원래의 객체형으로 변환 후 사용한다.
				Member mem = (Member) obj;
				
				System.out.println("이름 : "+mem.getName());
				System.out.println("나이 : "+mem.getAge());
				System.out.println("주소 : "+mem.getAddr());
				System.out.println();
			}
			
			System.out.println("반복문 다음 문장 출력");
			oin.close();
			
//		}catch (EOFException e) {
//			System.out.println("작업 완료...");
		} catch (ClassNotFoundException e) {	// readObject() 오류 검출
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		} 
	}
}

만약 age가 transient로 선언될 경우

package kr.or.ddit.basic;

import java.io.Serializable;

public class Member implements Serializable {
	
	// transient ==> 직렬화가 되지 않을 멤버 변수에 지정한다.
	private String name;
	private transient int age;
	private String addr;
	
	public Member(String name, int age, String addr) {
		super();
		this.name = name;
		this.age = age;
		this.addr = addr;
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public String getAddr() {
		return addr;
	}
	public void setAddr(String addr) {
		this.addr = addr;
	}
	
	
}

직렬화 대상에서 빠지게 되므로 data에 저장이 되지않아

모든 데이터의 age가 0으로 저장이된다