관리 메뉴

거니의 velog

230907_입출력 3 본문

대덕인재개발원_자바기반 애플리케이션

230907_입출력 3

Unlimited00 2023. 9. 7. 13:23

[DataIOTest.java]

package kr.or.ddit.basic;

import java.io.DataOutputStream;
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/test.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) {
			e.printStackTrace();
		}
		
	}

}


https://mh-nexus.de/en/hxd/

package kr.or.ddit.basic;

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/test.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) {
			e.printStackTrace();
		}
		
		//----------------------------------------------------------
		
		// 출력한 자료 읽어오기
		try {
			FileInputStream fin = new FileInputStream("d:/d_other/test.dat");
			
			// 기본 자료형 단위로 입력할 보조 스트림 객체 생성
			DataInputStream din = new DataInputStream(fin);
			
			// DataInputStream 으로 자료를 읽어올 때는 
			// DataOutputStream 으로 출력했을 때의 순서와 같은 순서로 읽어와야 된다.
			System.out.println("정수형 : " + din.readInt());
			System.out.println("실수형 : " + din.readFloat());
			System.out.println("논리형 : " + din.readBoolean());
			System.out.println("문자열 : " + din.readUTF());
			
			System.out.println("읽기 작업 완료");
			
			din.close(); // 스트림 닫기
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		
	}

}


[ObjectIOTest.java]

package kr.or.ddit.basic;

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

public class ObjectIOTest {

	public static void main(String[] args) {
		
		// Member의 인스턴스 생성
		Member mem1 = new Member("홍길동", 20, "대전");
		Member mem2 = new Member("홍길서", 30, "인천");
		Member mem3 = new Member("홍길남", 40, "강릉");
		Member mem4 = new Member("홍길북", 50, "포항");
		
		// 객체를 파일에 저장하기
		try {
			// 출력용 스트림 객체 생성
			FileOutputStream fout = new FileOutputStream("d:/d_other/member.obj");
			BufferedOutputStream bout = new BufferedOutputStream(fout);
			ObjectOutputStream oout = new ObjectOutputStream(bout);
			
			// 출력 작업
			System.out.println("객체 저장 작업 시작...");
			oout.writeObject(mem1); // java.io.NotSerializableException: kr.or.ddit.basic.Member
			oout.writeObject(mem2);
			oout.writeObject(mem3);
			oout.writeObject(mem4);
			System.out.println("객체 저장 작업 완료...");
			
			oout.close(); // 스트림 닫기
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		//------------------------------------------------------------
		
		// 저장된 객체를 읽어와 화면에 출력하기
		try {
			
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}

}

class Member implements Serializable {
	
	private String name;
	private int age;
	private String addr;
	
	// 생성자
	public Member(String name, int age, String addr) {
		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;
	}

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

package kr.or.ddit.basic;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.EOFException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class ObjectIOTest {

	public static void main(String[] args) {
		
		// Member의 인스턴스 생성
		Member mem1 = new Member("홍길동", 20, "대전");
		Member mem2 = new Member("홍길서", 30, "인천");
		Member mem3 = new Member("홍길남", 40, "강릉");
		Member mem4 = new Member("홍길북", 50, "포항");
		
		// 객체를 파일에 저장하기
		try {
			// 출력용 스트림 객체 생성
			FileOutputStream fout = new FileOutputStream("d:/d_other/member.obj");
			BufferedOutputStream bout = new BufferedOutputStream(fout);
			ObjectOutputStream oout = new ObjectOutputStream(bout);
			
			// 출력 작업
			System.out.println("객체 저장 작업 시작...");
			oout.writeObject(mem1); // java.io.NotSerializableException: kr.or.ddit.basic.Member
			oout.writeObject(mem2);
			oout.writeObject(mem3);
			oout.writeObject(mem4);
			
			// readObject()메서드의 EOFException을 방지하는 방법
			// 객체를 저장할 때 마지막에 null을 저장한다.
			oout.writeObject(null);
			System.out.println("객체 저장 작업 완료...");
			
			oout.close(); // 스트림 닫기
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		//------------------------------------------------------------
		
		// 저장된 객체를 읽어와 화면에 출력하기
		try {
			// 입력용 스트림 객체 생성
			ObjectInputStream oin = new ObjectInputStream(
					new BufferedInputStream(
						new FileInputStream("d:/d_other/member.obj")
					)
				);
			
			Object obj; // 읽어온 객체를 저장할 변수
			
			// readObject()메서드가 데이터를 끝까지 다 읽어오면 
			// EOFException이 발생한다.
			// EOF : End of File의 약자
			System.out.println("----------------------------------");
			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("----------------------------------");
			}
			
		// readObject()메서드의 EOFException을 처리하는 방법
		// ==> catch블럭에서 EOFException처리를 한다.
		} catch (EOFException e) {
			System.out.println("출력 끝...");
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		
	}

}

class Member implements Serializable {
	
	// transient ==> 직렬화가 되지 않을 멤버변수에 지정한다.
	// 			 	 (직렬화 되지 않는 변수는 기본값으로 초기화 된다.)
	//				 (기본값 ==> 참조형변수 : null, 숫자유형변수 : 0)
	private String name;
	private transient int age; // 나이만 직렬화를 배제하여 정보를 숨긴다.
	private transient String addr;
	
	// 생성자
	public Member(String name, int age, String addr) {
		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;
	}

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


[PhoneBookTest.java]

package kr.or.ddit.basic;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;

/*
 * 문제) 이름, 주소, 전화번호를 멤버로 갖는 Phone 클래스를 만들고,
 * 		Map을 이용하여 전화번호 정보를 관리하는 프로그램을 작성하시오.
 * 
 * 		(Map의 구조는 Key값으로 입력한 '이름'을 사용하고, value값으로는 'Phone클래스의 인스턴스'로 한다.)
 * 		변수 선언 예시) HashMap<String, Phone> 변수명;
 * 
 * 		이 프로그램에는 다음과 같은 메뉴가 있는데 각 메뉴의 기능을 구현한다.
 * 		-----------------
 * 		다음 메뉴를 선택하세요.
 * 		1. 전화번호 등록
 * 		2. 전화번호 수정
 * 		3. 전화번호 삭제
 * 		4. 전화번호 검색
 * 		5. 전화번호 전체 출력
 * 		0. 프로그램 종료
 * 		-----------------
 * 
 * 		- 검색, 삭제 기능은 '이름'을 입력 받아 처리한다.
 * 
 * 추가 조건)
 * 		1) '6. 전화번호 저장' 메뉴를 추가하고 구현한다.
 * 			(저장파일명은 'phoneBookData.dat'로 한다.)
 * 
 * 		2) 프로그램이 시작될 때 저장된 파일이 있으면 그 데이터를 읽어와 Map에 셋팅한다.
 * 
 * 		3) 프로그램을 종료할 때 Map의 데이터가 변경(추가, 수정, 삭제 등) 되면
 *			저장 후 종료되도록 한다.
 * 
 * 실행 예시)
 * 		-----------------
 * 		다음 메뉴를 선택하세요.
 * 		1. 전화번호 등록
 * 		2. 전화번호 수정
 * 		3. 전화번호 삭제
 * 		4. 전화번호 검색
 * 		5. 전화번호 전체 출력
 * 		0. 프로그램 종료
 * 		-----------------
 * 		메뉴선택 >> 1
 * 
 * 		새롭게 등록할 전화번호 정보를 입력하세요.
 * 		이     름 >> 홍길동
 * 		전화번호 >> 010-1234-5678
 * 		주     소 >> 대전시 중구 오류동
 * 
 * 		'홍길동' 전화번호 등록 완료!!!
 * 		
 * 		-----------------
 * 		다음 메뉴를 선택하세요.
 * 		1. 전화번호 등록
 * 		2. 전화번호 수정
 * 		3. 전화번호 삭제
 * 		4. 전화번호 검색
 * 		5. 전화번호 전체 출력
 * 		0. 프로그램 종료
 * 		-----------------
 * 		메뉴선택 >> 1
 * 
 * 		새롭게 등록할 전화번호 정보를 입력하세요.
 * 		이     름 >> 홍길동
 * 		
 * 		'홍길동'은 이미 등록된 사람입니다...
 * 
 * 		-----------------
 * 		다음 메뉴를 선택하세요.
 * 		1. 전화번호 등록
 * 		2. 전화번호 수정
 * 		3. 전화번호 삭제
 * 		4. 전화번호 검색
 * 		5. 전화번호 전체 출력
 * 		0. 프로그램 종료
 * 		-----------------
 * 		메뉴선택 >> 5
 * 
 * 		----------------------------------------
 * 		번호      이름       전화번호               주소
 * 		----------------------------------------
 * 		1          홍길동    010-1234-5678  대전시 중구 오류동
 * 		~~~
 * 		~~~
 * 		----------------------------------------
 * 		출력 끝...
 * 
 * 		-----------------
 * 		다음 메뉴를 선택하세요.
 * 		1. 전화번호 등록
 * 		2. 전화번호 수정
 * 		3. 전화번호 삭제
 * 		4. 전화번호 검색
 * 		5. 전화번호 전체 출력
 * 		0. 프로그램 종료
 * 		-----------------
 * 		메뉴선택 >> 0
 * 
 * 		프로그램을 종료합니다.
 * 
 */
public class PhoneBookTest {
	
	private HashMap<String, Phone2> phoneBookMap;
	private Scanner sc;
	private String fileName = "d:/d_other/phoneBookData.dat";
	
	// 데이터가 변경되었는지 여부를 나타내는 변수 선언
	// 데이터가 변경되면 이 변수값이 true가 된다.
	private boolean dataChange;
	
	// 생성자
	public PhoneBookTest() {
//		phoneBookMap = new HashMap<String, Phone2>();
		
		// 생성자에서 데이터를 읽어오는 작업을 해야 함
		phoneBookMap = load(); // 파일 내용을 읽어와 Map 객체에 셋팅한다.
		
		if(phoneBookMap == null) { // 파일이 없거나 입출력 오류일 때...
			phoneBookMap = new HashMap<String, Phone2>();
		}
		
		sc = new Scanner(System.in);
	}
	
	public static void main(String[] args) {
		
		new PhoneBookTest().startPhoneBook();
		
	}
	
	// 저장된 전화번호 정보를 읽어와서 반환하는 메서드
	private HashMap<String, Phone2> load() {
		// 읽어온 데이터가 저장될 변수 선언
		HashMap<String, Phone2> pMap = null;
		
		File file = new File(fileName);
		if(!file.exists()) { // 저장된 파일이 없다면...
			return null;
		}
		
		// 저장된 파일이 있으면 저장된 파일을 읽어오기 위한 스트림 객체 변수 선언
		ObjectInputStream oin = null;
		try {
			// 파일 입력용 스트림 객체 선언
			oin = new ObjectInputStream(
						new BufferedInputStream(
							new FileInputStream(file)
						)
					);
			
			// 파일 내용을 읽어와 Map 객체 변수에 저장한다.
			pMap = (HashMap<String, Phone2>) oin.readObject();
			
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} finally {
			if(oin != null) {
				try {
					oin.close();
				} catch (Exception e2) {}
			}
		}
		
		return pMap;
	}
	
	// 프로그램을 시작하는 메서드
	public void startPhoneBook() {
		System.out.println("************************");
		System.out.println(" 전 화 번 호 관 리 프 로 그 램");
		System.out.println("************************");
		while(true) {
			int choice = displayMenu();
			
			switch(choice) {
			case 1: // 전화번호 등록
				insert(); break;
			case 2: // 전화번호 수정
				update(); break;
			case 3: // 전화번호 삭제
				delete(); break;
			case 4: // 전화번호 검색
				search(); break;
			case 5: // 전화번호 전체 출력
				displayAll(); break;
			case 6: // 전화번호 저장
				save(); break;
			case 0: // 프로그램 종료
				if(dataChange == true) { // 데이터가 변경되었으면...
					System.out.println("변경된 데이터를 저장합니다.");
					save();
				}
				System.out.println("프로그램을 종료합니다.");
				return;
			default: 
				System.out.println("작업 번호를 잘못 입력했습니다.");
				System.out.println("다시 선택하세요...");
			}
		}
	}
	
	// 메뉴를 출력하고 작업 번호를 입력받아 반환하는 메서드
	private int displayMenu() {
		System.out.println();
		System.out.println("-----------------");
		System.out.println("다음 메뉴를 선택하세요.");
		System.out.println("1. 전화번호 등록");
		System.out.println("2. 전화번호 수정");
		System.out.println("3. 전화번호 삭제");
		System.out.println("4. 전화번호 검색");
		System.out.println("5. 전화번호 전체 출력");
		System.out.println("6. 전화번호 저장");
		System.out.println("0. 프로그램 종료");
		System.out.println("-----------------");
		System.out.print("메뉴선택 >> ");
		return sc.nextInt();
	}
	
	// 새로운 전화번호 정보를 등록하는 메서드 (이미 등록된 사람은 등록되지 않는다.)
	private void insert() {
		System.out.println();
		System.out.println("새로운 전화번호 정보를 입력하세요...");
		
		System.out.print("이 름 >> ");
		String name = sc.next();
		
		// 입력받은 이름이 이미 등록된 이름인지 검사
		if(phoneBookMap.containsKey(name)) {
			System.out.println(name + "씨는 이미 등록된 사람입니다...");
			System.out.println("등록 작업을 마칩니다.");
			return;
		}
		
		System.out.print("전화번호 >> ");
		String tel = sc.next();
		
		sc.nextLine(); // 일종의 입력 버퍼 비우기
		System.out.print("주 소 >> ");
		// String addr = sc.next(); // '대전시 중구 오류동' 중에 대전시만 가져가고 [데이터구분자 : 사이띄기, tab키, enter키], '중구 오류동' 중에 중구를 가져다 displayMenu()의 return sc.nextInt();와 충돌을 일으켜 오류가 뜸.
		String addr = sc.nextLine(); // 처음 문장부터 enter키 이전까지 모두 가져옴. enter 키를 제외한 값을 반환함.
		// String tel = sc.next(); 에 010-1234-1234[엔터키] 중에 010-1234-1234만 가져가고 구분자 [엔터키]를 남김. 
		// 이 구분자 [엔터키]를 String addr = sc.nextLine();이 인식해서 값을 입력받지 않고 그냥 넘어가 버림.
		// 따라서 실제 주소를 입력받기 전에 이 찌꺼기(구분자 [엔터키])를 비워줘야 함.
		// sc.nextLine(); 입력키를 한 번더 입력해 주면 끝.
		
		/*
		 * - Scanner객체는 입력 버퍼를 이용하여 입력 작업을 수행한다.
		 * 		즉, 입력 버퍼를 확인하여 값이 있으면 현재 입력 버퍼에 있는 값을 가져가고
		 * 		없으면 새로운 데이터를 입력받은 후 가져간다.
		 * 
		 * - Scanner의 next(), nextInt(), nextDouble() ... 등 (nextLine()을 제외한 메서드)
		 * 		==> 이 메서드들은 사이띄기, Tab키, Enter키를 구분 문자로 분리해서 분리된 자료만 읽어간다.
		 * 
		 * - Scanner의 nextLine()메서드
		 * 		==> 한 줄 단위로 입력한다. (즉, 자료를 입력하고 Enter키를 누르면 Enter키까지 읽어간다.)
		 */
		
		// 입력 받은 정보를 이용하여 Phone2 객체 생성
//		Phone2 p = new Phone2(name, tel, addr);
//		phoneBookMap.put(name, p);
		
		phoneBookMap.put(name, new Phone2(name, tel, addr));
		
		System.out.println(name + "씨의 전화번호 정보 등록 완료!!!");
		
		dataChange = true; // 전화번호 추가로 데이터 변경 표시
	}
	
	// 전체 전화번호 정보를 출력하는 메서드
	private void displayAll() {
		System.out.println();
		System.out.println("----------------------------------------");
		System.out.println("번호      이름       전화번호               주소");
		System.out.println("----------------------------------------");
		
		Set<String> phoneBookSet = phoneBookMap.keySet();
		if(phoneBookSet.size() == 0) {
			System.out.println("등록된 전화번호 정보가 하나도 없습니다...");
		}else {
			int cnt = 0; // 번호 출력용 변수
			
			Iterator<String> it = phoneBookSet.iterator();
			while(it.hasNext()) {
				cnt++;
				String key = it.next(); // 키 값(등록된 사람 이름)
				Phone2 p = phoneBookMap.get(key); // Value값(Phone2 객체)
				
				System.out.println(" " + cnt + "\t" + p.getName() + "\t" + p.getTel() + "\t" + p.getAddr());
			}
		}
		
		System.out.println("----------------------------------------");
		System.out.println("출력 끝...");
	}
	
	// 전화번호를 수정하는 메서드
	private void update() {
		System.out.println();
		System.out.println("수정할 전화번호 정보를 입력하세요...");
		System.out.print("이 름 >> ");
		String name = sc.next();
		
		// 해당 이름이 없으면 수정 작업을 중단한다.
		if(!phoneBookMap.containsKey(name)) {
			System.out.println(name + "씨의 전화번호 정보는 등록되지 않았습니다.");
			System.out.println("수정 작업을 마칩니다...");
			return;
		}
		
		System.out.print("새로운 전화번호 >> ");
		String newTel = sc.next();
		
		sc.nextLine(); // 일종의 입력 버퍼 비우기
		System.out.print("새로운 주소 >> ");
		// String newAddr = sc.next();
		String newAddr = sc.nextLine();
		
		// 같은 key값에 새로운 데이터를 셋팅한 Phone2 객체를 저장하면 된다.
		phoneBookMap.put(name, new Phone2(name, newTel, newAddr));
		
		System.out.println(name + "씨의 전화번호 정보 수정 완료!!!");
		
		dataChange = true; // 전화번호 수정으로 데이터 변경 표시
	}
	
	// 전화번호 정보를 삭제하는 메서드
	private void delete() {
		System.out.println();
		System.out.println("삭제할 전화번호 정보를 입력하세요.");
		System.out.print("이 름 >> ");
		String name = sc.next();
		
		// 해당 이름이 없으면 삭제 작업을 중단한다.
		if(!phoneBookMap.containsKey(name)) {
			System.out.println(name + "씨의 전화번호 정보는 등록되지 않았습니다.");
			System.out.println("삭제 작업을 마칩니다...");
			return;
		}
		
		phoneBookMap.remove(name); // 삭제작업
		
		System.out.println(name + "씨의 전화번호 정보를 삭제했습니다...");
		
		dataChange = true; // 전화번호 삭제로 데이터 변경 표시
	}
	
	// 전화번호 정보를 검색하는 메서드
	private void search() {
		System.out.println();
		System.out.println("검색할 전화번호 정보를 입력하세요...");
		System.out.print("이 름 >> ");
		String name = sc.next();
		
		// 입력한 이름을 key값으로 Phone2 객체 가져오기
		Phone2 p = phoneBookMap.get(name);
		
		if(p==null) { // 해당 key값의 데이터가 없으면...
			System.out.println(name + "씨의 전화번호 정보가 없습니다...");
		}else {
			System.out.println();
			System.out.println(name + "씨의 전화번호 정보");
			System.out.println("-----------------------");
			System.out.println("이     름 : " + p.getName());
			System.out.println("전화번호 : " + p.getTel());
			System.out.println("주     소 : " + p.getAddr());
			System.out.println("-----------------------");
		}
	}
	
	// 전화번호 정보를 파일로 저장하는 메서드
	private void save() {
		ObjectOutputStream oout = null;
		try {
			// 객체 저장용 출력 스트림 객체 생성
			oout = new ObjectOutputStream(
						new BufferedOutputStream(
							new FileOutputStream(fileName)
						)
					);
			
			// Map 객체를 파일로 저장한다.
			oout.writeObject(phoneBookMap);
			
			System.out.println("전화번호 파일 저장이 완료되었습니다...");
			
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			// 사용했던 스트림 객체 닫기
			if(oout != null) {
				try {
					oout.close();
				} catch (Exception e2) {}
			}
		}
		dataChange = false; // 데이터를 저장했으므로 굳이 프로그램 종료시 자동저장을 할 필요가 없다.
	}
	
}

// 이름, 주소, 전화번호를 멤버로 갖는 Phone 클래스를 만들기
class Phone2 implements Serializable {
	
	private static final long serialVersionUID = 1L;
	private String name;
	private String tel;
	private String addr;
	
	public Phone2() {}
	public Phone2(String name, String tel, String addr) {
		this.name = name;
		this.tel = tel;
		this.addr = addr;
	}
	
	public String getName() {
		return name;
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public String getTel() {
		return tel;
	}
	
	public void setTel(String tel) {
		this.tel = tel;
	}
	
	public String getAddr() {
		return addr;
	}
	
	public void setAddr(String addr) {
		this.addr = addr;
	}
	
	@Override
	public String toString() {
		return "Phone2 [name=" + name + ", tel=" + tel + ", addr=" + addr + "]";
	}
	
	public static long getSerialVersionUid() {
		return serialVersionUID;
	}
	
}


[PhoneBookTest2.java]

package kr.or.ddit.basic;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;

/*
 * 문제) 이름, 주소, 전화번호를 멤버로 갖는 Phone 클래스를 만들고,
 * 		Map을 이용하여 전화번호 정보를 관리하는 프로그램을 작성하시오.
 * 
 * 		(Map의 구조는 Key값으로 입력한 '이름'을 사용하고, value값으로는 'Phone클래스의 인스턴스'로 한다.)
 * 		변수 선언 예시) HashMap<String, Phone> 변수명;
 * 
 * 		이 프로그램에는 다음과 같은 메뉴가 있는데 각 메뉴의 기능을 구현한다.
 * 		-----------------
 * 		다음 메뉴를 선택하세요.
 * 		1. 전화번호 등록
 * 		2. 전화번호 수정
 * 		3. 전화번호 삭제
 * 		4. 전화번호 검색
 * 		5. 전화번호 전체 출력
 * 		0. 프로그램 종료
 * 		-----------------
 * 
 * 		- 검색, 삭제 기능은 '이름'을 입력 받아 처리한다.
 * 
 * 추가 조건)
 * 		1) '6. 전화번호 저장' 메뉴를 추가하고 구현한다.
 * 			(저장파일명은 'phoneBookData.dat'로 한다.)
 * 
 * 		2) 프로그램이 시작될 때 저장된 파일이 있으면 그 데이터를 읽어와 Map에 셋팅한다.
 * 
 * 		3) 프로그램을 종료할 때 Map의 데이터가 변경(추가, 수정, 삭제 등) 되면
 *			저장 후 종료되도록 한다.
 * 
 * 실행 예시)
 * 		-----------------
 * 		다음 메뉴를 선택하세요.
 * 		1. 전화번호 등록
 * 		2. 전화번호 수정
 * 		3. 전화번호 삭제
 * 		4. 전화번호 검색
 * 		5. 전화번호 전체 출력
 * 		0. 프로그램 종료
 * 		-----------------
 * 		메뉴선택 >> 1
 * 
 * 		새롭게 등록할 전화번호 정보를 입력하세요.
 * 		이     름 >> 홍길동
 * 		전화번호 >> 010-1234-5678
 * 		주     소 >> 대전시 중구 오류동
 * 
 * 		'홍길동' 전화번호 등록 완료!!!
 * 		
 * 		-----------------
 * 		다음 메뉴를 선택하세요.
 * 		1. 전화번호 등록
 * 		2. 전화번호 수정
 * 		3. 전화번호 삭제
 * 		4. 전화번호 검색
 * 		5. 전화번호 전체 출력
 * 		0. 프로그램 종료
 * 		-----------------
 * 		메뉴선택 >> 1
 * 
 * 		새롭게 등록할 전화번호 정보를 입력하세요.
 * 		이     름 >> 홍길동
 * 		
 * 		'홍길동'은 이미 등록된 사람입니다...
 * 
 * 		-----------------
 * 		다음 메뉴를 선택하세요.
 * 		1. 전화번호 등록
 * 		2. 전화번호 수정
 * 		3. 전화번호 삭제
 * 		4. 전화번호 검색
 * 		5. 전화번호 전체 출력
 * 		0. 프로그램 종료
 * 		-----------------
 * 		메뉴선택 >> 5
 * 
 * 		----------------------------------------
 * 		번호      이름       전화번호               주소
 * 		----------------------------------------
 * 		1          홍길동    010-1234-5678  대전시 중구 오류동
 * 		~~~
 * 		~~~
 * 		----------------------------------------
 * 		출력 끝...
 * 
 * 		-----------------
 * 		다음 메뉴를 선택하세요.
 * 		1. 전화번호 등록
 * 		2. 전화번호 수정
 * 		3. 전화번호 삭제
 * 		4. 전화번호 검색
 * 		5. 전화번호 전체 출력
 * 		0. 프로그램 종료
 * 		-----------------
 * 		메뉴선택 >> 0
 * 
 * 		프로그램을 종료합니다.
 * 
 */
public class PhoneBookTest2 {
	
	private HashMap<String, Phone3> phoneBookMap;
	private Scanner sc;
	
	// 저장 파일명
	private String fileName = "d:/d_other/phoneBookData.dat";
	
	// 데이터가 변경되었는지 여부를 나타내는 변수
	// (데이터가 변경되면 true, 그렇지 않으면 false값을 갖는다.)
	private boolean dataChange;
	
	// 생성자
	public PhoneBookTest2() {
//		phoneBookMap = new HashMap<String, Phone3>();
		sc = new Scanner(System.in);
		load();
	}
	
	public static void main(String[] args) {
		
		new PhoneBookTest2().startPhoneBook();
		
	}
	
	// 전화번호 정보가 저장된 파일을 읽어오는 메서드
	public void load() {
		
		// Map 객체를 새로 생성한다.
		phoneBookMap = new HashMap<String, Phone3>();
		
		File file = new File(fileName);
		
		// 저장된 파일이 있는지 검사
		if(!file.exists()) { // 저장된 파일이 없으면...
			return;
		}
		
		// 저장된 파일이 있으면 파일을 읽어와서  Map에 셋팅한다.
		ObjectInputStream oin = null;
		try {
			// 스트림 객체 생성
			oin = new ObjectInputStream(new BufferedInputStream(new FileInputStream(fileName)));
			
			// 파일 내용 읽어오기
			Object obj;
			
			// 방법1 ==> Phone3 객체를 하나씩 저장했을 때
//			while( (obj = oin.readObject()) != null ) {
//				Phone3 p = (Phone3) obj; // 읽어온 데이터를 형변환 한다.
//				phoneBookMap.put(p.getName(), p); // 읽어온 데이터를 Map에 셋팅한다.
//			}
					
			// 방법2 ==> Map객체 자체를 저장했을 때
			obj = oin.readObject();
			phoneBookMap = (HashMap<String, Phone3>) obj;
			
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	// 프로그램을 시작하는 메서드
	public void startPhoneBook() {
		System.out.println("*****************************");
		System.out.println(" 전 화 번 호 관 리 프 로 그 램 (저장가능)");
		System.out.println("*****************************");
		while(true) {
			int choice = displayMenu();
			
			switch(choice) {
			case 1: // 전화번호 등록
				insert(); break;
			case 2: // 전화번호 수정
				update(); break;
			case 3: // 전화번호 삭제
				delete(); break;
			case 4: // 전화번호 검색
				search(); break;
			case 5: // 전화번호 전체 출력
				displayAll(); break;
			case 6: // 전화번호 저장
				save(); break;
			case 0: // 프로그램 종료
				if(dataChange == true) {
					save();
				}
				System.out.println("프로그램을 종료합니다.");
				return;
			default: 
				System.out.println("작업 번호를 잘못 입력했습니다.");
				System.out.println("다시 선택하세요...");
			}
		}
	}
	
	// 메뉴를 출력하고 작업 번호를 입력받아 반환하는 메서드
	private int displayMenu() {
		System.out.println();
		System.out.println("-----------------");
		System.out.println("다음 메뉴를 선택하세요.");
		System.out.println("1. 전화번호 등록");
		System.out.println("2. 전화번호 수정");
		System.out.println("3. 전화번호 삭제");
		System.out.println("4. 전화번호 검색");
		System.out.println("5. 전화번호 전체 출력");
		System.out.println("6. 전화번호 저장");
		System.out.println("0. 프로그램 종료");
		System.out.println("-----------------");
		System.out.print("메뉴선택 >> ");
		return sc.nextInt();
	}
	
	// 새로운 전화번호 정보를 등록하는 메서드 (이미 등록된 사람은 등록되지 않는다.)
	private void insert() {
		System.out.println();
		System.out.println("새로운 전화번호 정보를 입력하세요...");
		
		System.out.print("이 름 >> ");
		String name = sc.next();
		
		// 입력받은 이름이 이미 등록된 이름인지 검사
		if(phoneBookMap.containsKey(name)) {
			System.out.println(name + "씨는 이미 등록된 사람입니다...");
			System.out.println("등록 작업을 마칩니다.");
			return;
		}
		
		System.out.print("전화번호 >> ");
		String tel = sc.next();
		
		sc.nextLine(); // 일종의 입력 버퍼 비우기
		System.out.print("주 소 >> ");
		// String addr = sc.next(); // '대전시 중구 오류동' 중에 대전시만 가져가고 [데이터구분자 : 사이띄기, tab키, enter키], '중구 오류동' 중에 중구를 가져다 displayMenu()의 return sc.nextInt();와 충돌을 일으켜 오류가 뜸.
		String addr = sc.nextLine(); // 처음 문장부터 enter키 이전까지 모두 가져옴. enter 키를 제외한 값을 반환함.
		// String tel = sc.next(); 에 010-1234-1234[엔터키] 중에 010-1234-1234만 가져가고 구분자 [엔터키]를 남김. 
		// 이 구분자 [엔터키]를 String addr = sc.nextLine();이 인식해서 값을 입력받지 않고 그냥 넘어가 버림.
		// 따라서 실제 주소를 입력받기 전에 이 찌꺼기(구분자 [엔터키])를 비워줘야 함.
		// sc.nextLine(); 입력키를 한 번더 입력해 주면 끝.
		
		/*
		 * - Scanner객체는 입력 버퍼를 이용하여 입력 작업을 수행한다.
		 * 		즉, 입력 버퍼를 확인하여 값이 있으면 현재 입력 버퍼에 있는 값을 가져가고
		 * 		없으면 새로운 데이터를 입력받은 후 가져간다.
		 * 
		 * - Scanner의 next(), nextInt(), nextDouble() ... 등 (nextLine()을 제외한 메서드)
		 * 		==> 이 메서드들은 사이띄기, Tab키, Enter키를 구분 문자로 분리해서 분리된 자료만 읽어간다.
		 * 
		 * - Scanner의 nextLine()메서드
		 * 		==> 한 줄 단위로 입력한다. (즉, 자료를 입력하고 Enter키를 누르면 Enter키까지 읽어간다.)
		 */
		
		// 입력 받은 정보를 이용하여 Phone3 객체 생성
//		Phone3 p = new Phone3(name, tel, addr);
//		phoneBookMap.put(name, p);
		
		phoneBookMap.put(name, new Phone3(name, tel, addr));
		
		dataChange = true;
		
		System.out.println(name + "씨의 전화번호 정보 등록 완료!!!");
	}
	
	// 전체 전화번호 정보를 출력하는 메서드
	private void displayAll() {
		System.out.println();
		System.out.println("----------------------------------------");
		System.out.println("번호      이름       전화번호               주소");
		System.out.println("----------------------------------------");
		
		Set<String> phoneBookSet = phoneBookMap.keySet();
		if(phoneBookSet.size() == 0) {
			System.out.println("등록된 전화번호 정보가 하나도 없습니다...");
		}else {
			int cnt = 0; // 번호 출력용 변수
			
			Iterator<String> it = phoneBookSet.iterator();
			while(it.hasNext()) {
				cnt++;
				String key = it.next(); // 키 값(등록된 사람 이름)
				Phone3 p = phoneBookMap.get(key); // Value값(Phone3 객체)
				
				System.out.println(" " + cnt + "\t" + p.getName() + "\t" + p.getTel() + "\t" + p.getAddr());
			}
		}
		
		System.out.println("----------------------------------------");
		System.out.println("출력 끝...");
	}
	
	// 전화번호를 수정하는 메서드
	private void update() {
		System.out.println();
		System.out.println("수정할 전화번호 정보를 입력하세요...");
		System.out.print("이 름 >> ");
		String name = sc.next();
		
		// 해당 이름이 없으면 수정 작업을 중단한다.
		if(!phoneBookMap.containsKey(name)) {
			System.out.println(name + "씨의 전화번호 정보는 등록되지 않았습니다.");
			System.out.println("수정 작업을 마칩니다...");
			return;
		}
		
		System.out.print("새로운 전화번호 >> ");
		String newTel = sc.next();
		
		sc.nextLine(); // 일종의 입력 버퍼 비우기
		System.out.print("새로운 주소 >> ");
		// String newAddr = sc.next();
		String newAddr = sc.nextLine();
		
		// 같은 key값에 새로운 데이터를 셋팅한 Phone3 객체를 저장하면 된다.
		phoneBookMap.put(name, new Phone3(name, newTel, newAddr));
		
		dataChange = true;
		
		System.out.println(name + "씨의 전화번호 정보 수정 완료!!!");
	}
	
	// 전화번호 정보를 삭제하는 메서드
	private void delete() {
		System.out.println();
		System.out.println("삭제할 전화번호 정보를 입력하세요.");
		System.out.print("이 름 >> ");
		String name = sc.next();
		
		// 해당 이름이 없으면 삭제 작업을 중단한다.
		if(!phoneBookMap.containsKey(name)) {
			System.out.println(name + "씨의 전화번호 정보는 등록되지 않았습니다.");
			System.out.println("삭제 작업을 마칩니다...");
			return;
		}
		
		phoneBookMap.remove(name); // 삭제작업
		
		dataChange = true;
		
		System.out.println(name + "씨의 전화번호 정보를 삭제했습니다...");
	}
	
	// 전화번호 정보를 검색하는 메서드
	private void search() {
		System.out.println();
		System.out.println("검색할 전화번호 정보를 입력하세요...");
		System.out.print("이 름 >> ");
		String name = sc.next();
		
		// 입력한 이름을 key값으로 Phone3 객체 가져오기
		Phone3 p = phoneBookMap.get(name);
		
		if(p==null) { // 해당 key값의 데이터가 없으면...
			System.out.println(name + "씨의 전화번호 정보가 없습니다...");
		}else {
			System.out.println();
			System.out.println(name + "씨의 전화번호 정보");
			System.out.println("-----------------------");
			System.out.println("이     름 : " + p.getName());
			System.out.println("전화번호 : " + p.getTel());
			System.out.println("주     소 : " + p.getAddr());
			System.out.println("-----------------------");
		}
	}
	
	// 전화번호 정보를 저장하는 메서드
	private void save() {
		ObjectOutputStream oout = null;
		try {
			// 스트림 객체 생성
			oout = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(fileName)));
			
			// Map의 자료 저장하기
			
			// 방법 1 ==> Map에 저장된 Phone3 객체를 하나씩 파일로 저장하기
//			for(String name : phoneBookMap.keySet()) {
//				Phone3 p = phoneBookMap.get(name);
//				oout.writeObject(p);
//			}
//			// 모든 자료가 저장된 후 마지막에 null로 저장한다.
//			oout.writeObject(null);
			
			// 방법 2 ==> Map객체 자체를 파일로 저장하기
			oout.writeObject(phoneBookMap);
			
			dataChange = false;
			
			System.out.println("전화번호 파일 저장이 완료되었습니다...");
			
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if(oout != null) try { oout.close(); } catch (IOException e) {}
		}
		
	}
	
}

// 이름, 주소, 전화번호를 멤버로 갖는 Phone 클래스를 만들기
class Phone3 implements Serializable {
	
	private String name;
	private String tel;
	private String addr;
	
	public Phone3() {}
	public Phone3(String name, String tel, String addr) {
		this.name = name;
		this.tel = tel;
		this.addr = addr;
	}
	
	public String getName() {
		return name;
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public String getTel() {
		return tel;
	}
	
	public void setTel(String tel) {
		this.tel = tel;
	}
	
	public String getAddr() {
		return addr;
	}
	
	public void setAddr(String addr) {
		this.addr = addr;
	}
	
	@Override
	public String toString() {
		return "Phone3 [name=" + name + ", tel=" + tel + ", addr=" + addr + "]";
	}
	
}

'대덕인재개발원_자바기반 애플리케이션' 카테고리의 다른 글

230911_입출력 5  (0) 2023.09.11
230908_입출력 4  (0) 2023.09.08
230906_입출력 2  (0) 2023.09.06
230905_입출력 1  (0) 2023.09.04
230904_스레드 5  (0) 2023.09.04