공부

1011 (18일차)

삶은고구마 2023. 10. 11. 16:31

scanner로 입력받을때의 nextInt()의 상위클래스를 확인하면 여기서도 throw를 사용한다.

 


입출력(IO)

JVM 기준

데이터가 들어오면 입력(scanner), 나가면 출력(print).

 

node : 기본 (대상과 연결됨 필수,성능이 좀 떨어짐) / filter : 보조

바이트/ 문자 기반 마다 정해진 입출력 클래스가 있기 때문에 교차해서 사용할 수 없다.

nodeStream이 필수인 이유:

시스템 입출력과 직접 연결됨.  

보조스트림은 그런 필수 스트림을 감싸서 사용함.

 

-표준 입출력

-System.in : 표준입력 (사용자 키보드 입력)

-System:out : 표준출력 (콘솔 출력)

-System.err : 표준에러

 

Scanner가 나오기 전 까진 아래의 방법으로 입력을 받았다.

-1은 더 이상 읽어올 값이 없을때 반환되는 값.

try 
		{
			while((input = System.in.read())!=-1)
			{
				System.out.println(input);
				/**
				 * a
				 * 97 (알파벳 a)
				 * 13 \r (맨 앞으로 가라)
				 * 10 \n (개행)
				 * 13과 10은 엔터를 치면서 발생.
				 * 아스키코드표 참고
				 */
			}
		} 
		catch (IOException e) 
		{
			e.printStackTrace();
		}

 

-입출력 스트림 

표준 입출력을 제외한 모든 stream 클래스는 사용 후 

close() 통해 자원을 반납해야 한다. os내에서 사용되는 메모리라서 사용이 끝났으면 반환.

 

  상대적 경로 절대적 경로
기준 현재 디렉토리 기준
(프로젝트 root directory)
루트 디렉토리 기준
윈도우: C:\~
맥: /~
디렉토리 명에 한글/공백이 있으면 오류가 발생한다.

byte 예제 코드1

더보기
private void test1() 
	{
		FileInputStream fis=null;
		FileOutputStream fos = null;
		try 
		{
			
//			fis = new FileInputStream("abc/abc.txt");//생성
//			fos = new FileOutputStream("abc/abc_copy.txt",true);//뒤에 t,f는 이어붙이기 여부 확인 생략시 false
			
			fis = new FileInputStream("abc/abc.txt");//생성
			//이스케이핑 처리
			//fos = new FileOutputStream("C:\\Users\\user1\\Desktop\\abc_copy2.txt",true);
			fos = new FileOutputStream("C:/Users/user1/Desktop/abc_copy3.txt",true);
			//이어쓰기는 객체입출력에선 불가
			int data = 0;
			while((data=fis.read())!=-1)
			{
				System.out.println(data);
				/**
				 * 영문과 숫자 등은 1byte씩 처리되어서 온전히 출력되지만..
				 * 한글과 한자는 3byte가 합쳐져서 한글자가 되는거라(utf-8) 
				 * 1byte씩 읽어오면 아마 깨질것
				 */
				fos.write(data);
			}
		} 
		catch (IOException e) //FileNotFondException(자식)도 핸들링. 부모클래스라서.
		//하지만 NullFoninterException은 잡을수 없어서 Exception으로 해도됨
		{
			e.printStackTrace();
		}
		finally 
		{
			try 
			{
				fis.close();//try안에서 선언된 변수라 접근 못함..try위에서 선언.
				
			} 
			catch (Exception e) 
			{
				e.printStackTrace();
			} 
			
			try {
				fos.close();
				//fis.close(); 밑에 적으면 좋겠지만..예외는 따로 처리하는것이 좋다.
				//fis.close()에서 예외처리 당하면 아랫줄은 skip되기 때문임.
			} 
			catch (Exception e) 
			//IOException에서 Exception으로 바꾸는 이유. 
			//fos=null인상태에서 예외처리 하기 위함.io면 nullpointEx 감지 못함
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
	}

 

byte 말고 byte 배열로도 입출력 작업이 가능하다.

byte[] 예제 코드2

더보기
/**
	 * test2 개선
	 * -read():int
	 * -read(byte[]):int
	 * -try..with..resource(jdk 1.7) - Stream 객체의 선언과 반납 처리 (finally 불필요)
	 */
	private void test3() 
	{
		try (//대입x 선언
				FileInputStream fis = new FileInputStream("C:\\Users\\user1\\Desktop\\image\\nongdam.jpeg");
				FileOutputStream fos = new FileOutputStream("nongdam_copy.jpeg");
			){
			
			byte[] buf = new byte[8192]; //기본:8kb 다른 수치로도 가능.
			int len = 0; //read(byte[])의 반환값은 읽어낸 byte 갯수
			
			while((len=fis.read(buf))!=-1) //읽어올 값이 없으면 -1리턴
			{
				System.out.println(len);
				fos.write(buf,0,len); //buf의 idnex 0번부터 읽어낸 길이만큼 써라.
			}
		} 
		catch (IOException e)
		{
			e.printStackTrace();
		}
		
	}

 

보조 스트림

1.기본 스트림의 성능향상 또는 부가기능을 위해 n개를 추가해서 쓸 수있다.

2.반드시 기본 스트림이 필요함(표준 입출력과 직접 연결됨)

  보조 스트림 단독 사용 불가

3.기본스트림 객체를 감싼 구조. 보조스트림 객체만 제어하면 된다.

4.보조스트림 객체만 반환하면 기본 스트림까지 반환된다.

주의 사항: 입력-입력끼리, 문자기반-문자기반 끼리 사용 가능. 교차사용 불가

 

보조스트림 예제 코드

더보기
private void test4() 
	{
		String srcName = "C:\\Users\\user1\\Desktop\\image\\nongdam.jpeg";
		String destName = "nongdam_copy.jpeg";
		try 
		(
			//기본스트림이 인자로 반드시 있어야함
			//기본스트림=대상과 직접 연결
			//보조스트림=기본에 성능만 추가 srcName과 직접 연결 불가
			BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcName));
			BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(srcName));
			){
			
			byte[] buf = new byte[8192]; //기본:8kb
			int len = 0; //read(byte[])의 반환값은 읽어낸 byte 갯수
			
			//보조스트림 bis ,bos 만 사용
			while((len=bis.read(buf))!=-1) 
			{
				System.out.println(len);
				bos.write(buf,0,len); 
			}
		} 
		catch (IOException e)
		{
			e.printStackTrace();
		}
		
	}

 

문자 기반 스트림

한 글자 씩 처리한다.(한글 깨짐 없음) 텍스트 파일을 처리할 때 사용.

FileReader 문자기반 입력 스트림

FileWriter   문자기반 출력 스트림

예제 코드1

더보기
//문자 단위로 작업
	private void test1() 
	{
		//파일 객체를 만들어서 reader에 인자로 사용해도 됨
		//실제,가상의 파일을 가리키는 자바 객체 (파일 자체는 아님)
		//file 객체를 통해서 실제 파일을 제어하는 것이 가능함.
		//스트림도 파일 사용가능
		File src = new File("abc/abc.txt");
		File dest = new File("abc/abc_copy3.txt");
		try(//finally x
			FileReader fr = new FileReader(src);
			FileWriter fw = new FileWriter(dest);	
				
			){
			int data = 0;
			while((data = fr.read())!=-1)
			{
				System.out.print((char)data);
				fw.write(data);
			}
		}
		catch (IOException e) 
		{
			e.printStackTrace();
		}
	}

예제 코드1의 보조스트림 버전 코드

더보기
/**
	 * test1의 보조스트림 버전
	 * 입력스트림:읽어올 파일이 없으면 오류.(반드시 존재!!)
	 * 출력스트림:출력할 파일이 없어도 오류안남. 없으면 스트림클래스에서 새로 생성
	 */
	private void test2() 
	{
		File src = new File("abc/abc.txt");
		File dest = new File("abc/abc_copy4.txt"); //디렉토리는 반드시 존재 해야함...
		//경로없을 시:FileNotFoundException:abcz\abc_copy4.txt (지정된 경로를 찾을 수 없습니다)
		
		try(
				BufferedReader br = new BufferedReader(new FileReader(src));
				BufferedWriter bw = new BufferedWriter(new FileWriter(dest));
		)
		{
			String data =null;
			/**
			 * readLine():개행문자까지 읽어서 개행문자를 버리고 반환
			 */
			while((data=br.readLine())!=null) //readLine:한줄씩
			{
				System.out.print(data);
				bw.write(data+"\n"); //개행추가해야 원본과 동일
			}
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	
		
	}

브릿지 예제 코드

더보기
/**
	 * jdk1.5에 추가된 Scanner 이 전의 사용자 입력값..
	 * -System.in - BufferedReader 
	 * (byte - String 사이 브릿지 역할:inputStreamReader)
	 */
	private void test3() 
	{
		try
		{
			//스캐너와 다르게 예외처리 필수..
			//System.in:표준입력이 있어서 close()하지 않음.           //브릿지   //표준입출력
			BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
			System.out.print("아무말:");
			String str = br.readLine(); //sc.next() 처럼..
			System.out.printf("\"%s\"라고 하셨습니다.",str);
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}

 

-파일

java.io.File

1.실제 / 가상의 파일 , 디렉토리를 가리키는 자바 객체

2.파일 생성,삭제,메타정보 조회..

3.디렉토리 생성, 삭제, 자식 파일 조회 등등 쓰임새가 다양하다.

파일 객체 생성 File f = new File(경로); 
파일명 f.getName()
경로(파일명 제외) f.getParent()
절대 경로 f.getAbsolutePath()
존재 유무
(boolean)
f.exists()
파일인가?
(boolean)
f.isFile()
디렉토리인가?
(boolean)
f.isDirectory()
파일명 변경(이동) File f2 = new File(새 경로);
f.renameTo(f2);
파일 삭제 f.delete()
새 디렉토리 만들기
단일
dir.mkdir();
새 디렉토리 만들기
복수
dir.mkdirs();

파일 예제 코드1

더보기
private void test1() 
	{
		File f = new File("abc/abc.txt"); //상대경로
		System.out.println(f);//toString override
		
		File f2 = new File("C:\\Workspaces\\java_workspace\\14_IO\\abc\\abc.txt"); //절대경로
		System.out.println(f2);
		
		System.out.println(f2.getName()); //파일명만 보여줌
		System.out.println(f2.getParent()); //경로(파일명뺀)
		System.out.println(f.getAbsolutePath());//절대경로
		System.out.println(f.exists());//존재:true
		System.out.println(f.isFile());
		System.out.println(f.isDirectory());
		
		File f3 = new File("abc/def.txt");
		if(!f3.exists())
		{
			System.out.println(f3+"을 생성합니다.");
			try {
				f3.createNewFile();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		//파일명 변경 = 이동
		File dest = new File("def.txt");
		f3.renameTo(dest);
		
		//f3.delete();//파일삭제도 있다.
	}

파일 예제 코드2

사용자 입력값을 받아 폴더와 txt파일 만들기

더보기
/**
	 *  사용자 이름을 입력받아 members 하위에 사용자명/사용자명.txt 파일을 생성.
	 *  -members/홍길동/홍길동.txt
	 *  -members,사용자명 디렉토리를 동적으로 생성.
	 */
	private void test3()
	{
		Scanner sc = new Scanner(System.in);
		System.out.print("사용자 명을 입력하세요. : ");
		String userStr = sc.next();
		File dir = new File("C:\\Workspaces\\java_workspace\\14_IO");//절대경로

		
		File newDir = new File("members"); 
		if(!newDir.exists()) //존재하지 않는다면
		{
			newDir.mkdir(); 
			System.out.println(newDir+"생성완료");
		}
		File newDir2 = new File(newDir+"/"+userStr); //or (newDir,파일명);
		if(!newDir2.exists()) //존재하지 않는다면
		{
			newDir2.mkdir(); 
			System.out.println(newDir2+"생성완료");
		}
		File myFile = new File("members/"+userStr+"/"+userStr+".txt");
		if(!myFile.exists())
		{
			try {
				myFile.createNewFile();
				System.out.println(myFile+"을 생성합니다.");
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		
	}

 

-오브젝트 입출력

객체 단위로 읽고/쓰기를 지원하는 스트림(byte 기반)

1)ObjectInputStream

2)ObjectOutputStream

읽기/쓰기를 작업할 클래스는 반드시 serializable 인터페이스를 구현해야 한다.

serializable란?

추상 메소드가 없는 마커 인터페이스..(추후 따로 공부하기)

 

객체를 하나만 읽고 쓸 수도, 여러개를(리스트) 읽고 쓸 수 있다.

 

오브젝트 입출력 예제 코드 1

객체 하나만

더보기
/**
	 * -객체 하나 쓰기 읽기
	 */
	private void test1() {
		// TODO Auto-generated method stub
		try(
			ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.ser"));	
				)
		{
			oos.writeObject(new User("honggd","1234",LocalDateTime.now()));
			System.out.println("저장 완료!");
			//NotSerializableException
		
		} 
		catch (IOException e) 
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		
		}
		
		//
		try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.ser")))
		{
			User user = (User)ois.readObject();
			System.out.println(user);
		}
		catch (IOException | ClassNotFoundException e) 
		{	//멀티 캐치 : 부모자식간 사용 불가
			// TODO: handle exception
			e.printStackTrace();
		} 
				
	}

오브젝트 입출력 예제 코드 2

리스트를 사용하여 여러개를 다룬다.

더보기
/**
	 * -객체 List에 담아 입출력.
	 */
	private void test2() 
	{
		List<User> users = new ArrayList<>();
		users.add(new User("honggd","1234",LocalDateTime.of(2023,10,10, 10, 10)));
		users.add(new User("sinsa","1234",LocalDateTime.of(2023,10,9, 9, 9)));
		users.add(new User("leess","1234",LocalDateTime.of(2023,10,8, 8, 8)));
		
		//ObjectOutputStream - BufferedOutputStream - FileOutputStream
		try
		(
				ObjectOutputStream oos = 
						new ObjectOutputStream(
							new BufferedOutputStream(
								new FileOutputStream("users.ser")));
		)
		{
			oos.writeObject(users);
			System.out.println("저장 완료...");
		} 
		catch (IOException e) 
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

 

출력.

 

/**
	 * ObjectInputStream - BufferedInputStream - FileInputStream
	 */
	private void test3() {
		try(
				ObjectInputStream ois = 
				new ObjectInputStream(
						new BufferedInputStream(
							new FileInputStream("users.ser")));
				
				) 
		{
			List<User>users = (List<User>)ois.readObject();
			System.out.println(users);
		} catch (IOException | ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}

 

 

 

객체 입출력 시 사용한 User 클래스.

상단을 보면 Serializable을 구현하였다. 

안하면 에러나고, 구현했다면

시리얼유니크아이디를 부여하라고 알려주는데 이건 사용자가 원하는 값을 지정할수있음.

또한 비밀번호같이 보안을 요하는/민감한 필드인 경우 타입명앞에 transient 라는 키워드를 붙일 수있음

직렬화 작업에 포함되지 않아 입출력 하고나면 null 값 인걸 알 수 있다..

package sh.java.io.user;

import java.io.Serializable;
import java.time.LocalDateTime;

public class User implements Serializable{ //Serializable:마커 인터페이스
	/**
	 *  serialVersionUID 객체 입출력 시에 정확한 타입인지를 체크하는 식별값
	 *  -부여하지 않는다면 랜덤처리
	 */
	private static final long serialVersionUID = 1L;
	private String name;
	private transient String pw; 
	// 객체 입출력 시 값을 제외(필드는 존재하는데 값을 날림) 보안등의 문제로 제외하고싶을때..
    //파일에 저장할때부터 없음 null
	private LocalDateTime createAt;
	//만약 커스텀 변수면 이것도 Serializable를 참조해야함..
	//무조건 Serializable로 이루어져야함.
	
	
	public User(String name, String pw, LocalDateTime createAt)
	{
		this.name = name;
		this.pw = pw;
		this.createAt = createAt;
	}
	
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getPw() {
		return pw;
	}
	public void setPw(String pw) {
		this.pw = pw;
	}
	public LocalDateTime getCreateAt() {
		return createAt;
	}
	public void setCreateAt(LocalDateTime createAt) {
		this.createAt = createAt;
	}
	@Override
	public String toString() {
		return "User [name=" + name + ", pw=" + pw + ", createAt=" + createAt + "]";
	}
}

그 동안은 scanner와 system.out.print만 다뤄봤는데 

타입,목적에 따라 다양한 입출력 클래스를 사용할 수 있었다.

byte인지 string인지 혹은 객체를 사용할 것인지에 따라 표를 보고 사용해봐야겠다.

그리고 예외처리는 필수. 값을 입출력할때 비정상 종료 되는 일 없도록.