JAVA

[JAVA] 파일 입출력

shb 2022. 2. 22. 16:05

스트림 (stream)
[도착지]                                                    [출발지]
Program <--- InputStream <--- Source(Keyboard, Mouse, File, Network)

[출발지]                                                    [도착지]
Program ---> OutputStream ---> Destination(Monitor, Beam, File, Network)


java.io 패키지의 주요 클래스

1) 바이트(byte) 단위 입출력 스트림 클래스
java.io.InputStream: 프로그램이 '바이트 단위' 데이터를 읽어들이는(read) 통로
java.io.OutputStream: 프로그램이 '바이트 단위' 데이터를 쓰는(write) 통로
** 위 두개 클래스는 '추상클래스' 다

2) 문자(character) 단위 입출력 스트림 클래스
java.io.Reader: 프로그램이 '문자 단위' 데이터를 읽어들이는(read) 통로
java.io.Writer: 프로그램이 '문자 단위' 데이터를 쓰는(write) 통로

3) java.io.File : 파일 시스템의 파일정보를 얻기 위한 클래스

4) java.io.Console : 콘솔로부터 문자을 입출력하기 위한 클래스

 

public class File01Main {

	public static void main(String[] args) {
		System.out.println("IO(Input/Output)");
		
		// InputStream 객체 in을 가지고 읽어들일 수 있는 스캐너 객체 생성
		Scanner sc = new Scanner(System.in);

		// 외부장치(콘솔, 키보드)로부터 데이터를 읽음
		String msg = sc.nextLine();
		
		sc.close();
		
		
		// OutpustStream 객체인 out이 가지고 있는 println() 메소드를 사용
		// 외부장치(콘솔, 모니터)에 데이터를 씀
		System.out.println(msg);
		
		System.out.println("\n프로그램 종료");
		
	} // end main()

} // end class

* FileIO
 Program <=== InputStream <=== Source
 Program ===> OutputStream ===> Source
 Program <=== FileInputStream <=== File
 Program ===> FileOutputStream ===> File

 java.io.InputStream
  |__ java.io.FileInputStream: 파일로부터 데이터를 읽어오는 통로
 java.io.OutputStream
  |__ java.io.FileOutputStream: 파일로 데이터를 쓰는 통로

 

package com.lec.java.file02;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/* FileIO
 Program <=== InputStream <=== Source
 Program ===> OutputStream ===> Source
 Program <=== FileInputStream <=== File
 Program ===> FileOutputStream ===> File

 java.io.InputStream
  |__ java.io.FileInputStream: 파일로부터 데이터를 읽어오는 통로
 java.io.OutputStream
  |__ java.io.FileOutputStream: 파일로 데이터를 쓰는 통로
*/

public class File02Main {

	public static void main(String[] args) {
		System.out.println("File IO");

		InputStream in = null; 
		OutputStream out = null;
		
		try {
			// FileInputStream 인스턴스 생성
			in = new FileInputStream("temp/big_text.txt");
			
			// FileOutputStream 인스턴스 생성
			// 해당 파일이 없으면, 새로생성.
			//  ..     있었으면, 지우고 새로생성.
			out = new FileOutputStream("temp/copy_big_text.txt");
			
			// 파일복사
			// InputStream 에서 한 byte 씩 읽어 들여와서
			// OutputStream 에 한 byte 씩 쓰기.
			int dataRead;
			int byteCopied = 0;
			long startTime = System.currentTimeMillis();   // 현재 시간 저장
			while(true) {	
				// 데이터 읽기: InputStream에 있는 read() 메소드 사용
				// read()는 InputStream 으로부터 
				// 1byte 씩 읽어서 int(4byte) 에 담아 리턴한다
				// [ ... ][ ... ][ ... ][ 1byte ]				
				dataRead = in.read();
				if(dataRead == -1) {  // 더이상 읽을 것이 없으면 read() 는 -1 을 리턴한다.
					break;
				}
				
				// 데이터 쓰기: OutputStream에 있는 write() 메소드 사용
				// write() 는 
				// int(4byte)에 1byte씩 담아 OutputStream에 쓴다
				// [ ... ][ ... ][ ... ][ 1byte ]				
				out.write(dataRead);
				byteCopied++;
			} // end while
			
			long endTime = System.currentTimeMillis(); // 끝난 시간 저장 
			long elapsedTime = endTime - startTime; // 경과 시간
			
			System.out.println("읽고 쓴 바이트: " + byteCopied);
			System.out.println("경과 시간(ms): " + elapsedTime);
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			// 리소스 해제
			try {
				if(out != null) out.close();
				if(in != null) in.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		System.out.println("\n프로그램 종료");
		
	} // end main()

} // end class

 Java 7부터 도입된 try-with-resource
 try (리소스 생성) { ... }
 catch (exception ) { ... }
 리소스를 close하는 코드가 없어도 자동으로 close가 실행

 java.lang.AutoCloseable 나 
  └─ java.io.Closeable 을 상속받은 어떠한 객체라도 
  try(리소스 생성) 안에 '선언' 되어 있으면
  try~catch 가 끝나기 전에 close() 됨.

 InputStream, OutputStream 둘다 Closeable 을 상속(implements) 한다

package com.lec.java.file03;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/*
 java.io.InputStream
  |__ java.io.FileInputStream
 java.io.OutputStream
  |__ java.io.FileOutputStream
*/

public class File03Main {

	public static void main(String[] args) {
		System.out.println("File IO 2");
		
		// Java 7부터 도입된 try-with-resource
		// try (리소스 생성) { ... }
		// catch (exception ) { ... }
		// 리소스를 close하는 코드가 없어도 자동으로 close가 실행
		//
		// java.lang.AutoCloseable 나 
		//  └─ java.io.Closeable 을 상속받은 어떠한 객체라도 
		//  try(리소스 생성) 안에 '선언' 되어 있으면
		//  try~catch 가 끝나기 전에 close() 됨.
		
		// InputStream, OutputStream 둘다 Closeable 을 상속(implements) 한다
		
	
		try(
				// FileInputStream 생성
				InputStream in = new FileInputStream("temp/big_text.txt");
				OutputStream out = new FileOutputStream("temp/copy_big_text.txt");
				){
			
			byte[] buff = new byte[1024];   // 버퍼 준비
			
			int lengthRead = 0;  // 버퍼에 담긴 byte수
			int byteCopied = 0;
			
			long startTime = System.currentTimeMillis();
			
			// 파일복사
			while(true) {
				// 데이터 읽기
				
				// 매개변수로 주어진 byte[] 배열의 길이 만큼 read한다.
				// 실제 읽어 들인 데이터는 매개변수 byte[] 에 담김.
				// 읽어들인 바이트 개수 리턴,  읽어들인게 없으면 -1 리턴.				
				lengthRead = in.read(buff);
				if(lengthRead == -1) {  // 더이상 읽어들일 것이 없으면 종료
					break;
				}
				
				// 데이터 쓰기
				out.write(buff, 0, lengthRead);  // 직전에 읽어들인 데이터만큼 write
				
				byteCopied += lengthRead;
			}
			
			long endTime = System.currentTimeMillis();
			long elapsedTime = endTime - startTime;
			
			System.out.println("전체 복사한 바이트: " + byteCopied);
			System.out.println("경과 시간: " + elapsedTime);
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		System.out.println("\n프로그램 종료");
		
	} // end main()

} // end class

보조스트림 (filter stream)
Program <=== FilterInputStream <=== InputStream <=== Source
↓ 상속 ↓ 상속
Program <=== BufferedInputStream <=== FileInputStream <=== File

Program ===> FilterOutputStream ===> OutputStream ===> Source
↓ 상속 ↓ 상속
Program ===> BufferedOutputStream ===> FileOutputStream ===> File

java.io.InputStream
 |__ java.io.FilterInputStream
      |__ java.io.BufferedInputStream

java.io.OutputStream
 |__ java.io.FilterOutputStream
      |__ java.io.BufferedOutputStream

참고 ) 보조스트림 (filter stream)
보조스트림(filter stream) 이란 다른 스트림과 연결되어 여러가지 편리한 기능을 제공해주는 스트림

package com.lec.java.file04;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/* 보조스트림 (filter stream)
Program <=== FilterInputStream <=== InputStream <=== Source
					↓ 상속					↓ 상속
Program <=== BufferedInputStream <=== FileInputStream <=== File

Program ===> FilterOutputStream ===> OutputStream ===> Source
					↓ 상속					↓ 상속
Program ===> BufferedOutputStream ===> FileOutputStream ===> File

java.io.InputStream
 |__ java.io.FilterInputStream
      |__ java.io.BufferedInputStream

java.io.OutputStream
 |__ java.io.FilterOutputStream
      |__ java.io.BufferedOutputStream

참고 ) 보조스트림 (filter stream)
보조스트림(filter stream) 이란 다른 스트림과 연결되어 여러가지 편리한 기능을 제공해주는 스트림
*/

public class File04Main {

	public static void main(String[] args) {
		System.out.println("BufferedInputStream, BufferedOutputStream");

		InputStream in = null; 
		BufferedInputStream bin = null;
		OutputStream out = null;
		BufferedOutputStream bout = null;
		
		try {
			// FileInputStream 인스턴스 생성
			in = new FileInputStream("temp/big_text.txt");
			bin = new BufferedInputStream(in); // 장착!
			
			// FileOutputStream 인스턴스 생성
			// 해당 파일이 없으면, 새로생성.
			//  ..     있었으면, 지우고 새로생성.
			out = new FileOutputStream("temp/copy_big_text.txt");
			bout = new BufferedOutputStream(out);  // 장착!
			
			// in.read() 대신에 bin.read() 를
			// out.write() 대신에 bout.write() 를 사용하면 된다.
			// finally 꼭!
			
			// 파일복사
			int dataRead;
			int byteCopied = 0;
			long startTime = System.currentTimeMillis();   // 현재 시간 저장
			while(true) {	
				dataRead = bin.read();
				if(dataRead == -1) {
					break;
				}
				
				bout.write(dataRead);
				byteCopied++;
			} // end while
			
			long endTime = System.currentTimeMillis(); // 끝난 시간 저장 
			long elapsedTime = endTime - startTime; // 경과 시간
			
			System.out.println("읽고 쓴 바이트: " + byteCopied);
			System.out.println("경과 시간(ms): " + elapsedTime);
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			// 리소스 해제
			try {
				if(bout != null) bout.close();
				if(bin != null) bin.close();
				// bin 을 close() 하면 bin 이 장착된 in 도 함께 close된다.
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		System.out.println("\n프로그램 종료");
		
	} // end main()

} // end class
package com.lec.java.file05;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/* Buffered Stream + Buffer 예제
Program <=== FilterInputStream <=== InputStream <=== Source
					↓ 상속					↓ 상속
Program <=== BufferedInputStream <=== FileInputStream <=== File

Program ===> FilterOutputStream ===> OutputStream ===> Source
					↓ 상속					↓ 상속
Program ===> BufferedOutputStream ===> FileOutputStream ===> File

java.io.InputStream
 |__ java.io.FilterInputStream
      |__ java.io.BufferedInputStream

java.io.OutputStream
 |__ java.io.FilterOutputStream
      |__ java.io.BufferedOutputStream
*/

public class File05Main {

	public static void main(String[] args) {
		System.out.println("Buffered Stream + Buffer");
		
		// TODO:
		// file03 패키지 참조
		// try with resource 구문으로 작성
		// in.read(buff) --> bin.read(buff);
		// out.write( , , ) --> bout.write( , , ); 사용
		// finally 필요 없슴
		
		
		try(
				// FileInputStream 생성
				InputStream in = new FileInputStream("temp/big_text.txt");
				BufferedInputStream bin = new BufferedInputStream(in);  // 장착
				OutputStream out = new FileOutputStream("temp/copy_big_text.txt");
				BufferedOutputStream bout = new BufferedOutputStream(out); 
				){
			
			byte[] buff = new byte[1024];   // 버퍼 준비
			
			int lengthRead = 0;  // 버퍼에 담긴 byte수
			int byteCopied = 0;
			
			long startTime = System.currentTimeMillis();
			
			// 파일복사
			while(true) {
				// 데이터 읽기
				
				lengthRead = bin.read(buff);
				if(lengthRead == -1) {  // 더이상 읽어들일 것이 없으면 종료
					break;
				}
				
				// 데이터 쓰기
				bout.write(buff, 0, lengthRead);  // 직전에 읽어들인 데이터만큼 write
				
				byteCopied += lengthRead;
			}
			
			long endTime = System.currentTimeMillis();
			long elapsedTime = endTime - startTime;
			
			System.out.println("전체 복사한 바이트: " + byteCopied);
			System.out.println("경과 시간: " + elapsedTime);
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		
		System.out.println("\n프로그램 종료");

	} // end main()

} // end class File05Main
package com.lec.java.file06;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/*  Data Filter Stream
 Program <=== DataInputStream <=== FileInputStream <=== File
 Program ===> DataOutputStream ===> FileOutputStream ===> File

java.io.InputStream
|__ java.io.FilterInputStream
   |__ java.io.DataInputStream  

java.io.OutputStream
|__ java.io.FilterOutputStream
   |__ java.io.DataOutputStream
*/

public class File06Main {

	public static void main(String[] args) {
		System.out.println("Data Filter Stream");
		
		try(
				OutputStream out = new FileOutputStream("temp/data.bin");
				DataOutputStream dout = new DataOutputStream(out);
				InputStream in = new FileInputStream("temp/data.bin");
				DataInputStream din = new DataInputStream(in);
				){
			dout.writeBoolean(true);  // 1 byte
			dout.writeInt(100);    // 4 byte
			dout.writeDouble(3.14);  // 8 byte
			dout.writeChar('A');    // 2 byte
			
			boolean b = din.readBoolean();
			System.out.println("boolean: " + b);
			
			int num1 = din.readInt();
			System.out.println("int: " + num1);
			
			double num2 = din.readDouble();
			System.out.println("double: " + num2);
			
			char ch = din.readChar();
			System.out.println("char: " + ch);
			
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		System.out.println("\n프로그램 종료");
		
	} // end main()

} // end class

 Object Filter Stream
 Program <=== ObjectInputStream <=== FileInputStream <=== File
 Program ===> ObjectOutputStream ===> FileOutputStream ===> File

java.lang.Object
 └─ java.io.OutputStream
    └─ java.io.ObjectOutputStream
 
java.lang.Object
 └─ java.io.InputStream
     └─ java.io.ObjectInputStream


 Object Stream: 객체의 입출력을 위한 스트림
 사용법은 다른 Filter Stream(Buffered I/O, Data I/O)과 비슷
 Object 스트림의 입출력 대상이 되는 클래스는 Serializable 인터페이스를 구현
 클래스의 일부 멤버 변수를 직렬화(Serialization)의 대상에서 제외시키려면, transient 키워드를 사용

 

package com.lec.java.file08;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;

/* Object Filter Stream
 Program <=== ObjectInputStream <=== FileInputStream <=== File
 Program ===> ObjectOutputStream ===> FileOutputStream ===> File

java.lang.Object
 └─ java.io.OutputStream
    └─ java.io.ObjectOutputStream
 
java.lang.Object
 └─ java.io.InputStream
     └─ java.io.ObjectInputStream


 Object Stream: 객체의 입출력을 위한 스트림
 사용법은 다른 Filter Stream(Buffered I/O, Data I/O)과 비슷
 Object 스트림의 입출력 대상이 되는 클래스는 Serializable 인터페이스를 구현
 클래스의 일부 멤버 변수를 직렬화(Serialization)의 대상에서 제외시키려면,
 transient 키워드를 사용
*/
public class File08Main {
	
	public static final String FILEPATH  = "temp/member.dat";

	public static void main(String[] args) {
		System.out.println("Object Filter Stream");
		
		try(
				OutputStream out = new FileOutputStream(FILEPATH);
				ObjectOutputStream oout = new ObjectOutputStream(out);
				InputStream in = new FileInputStream(FILEPATH);
				ObjectInputStream oin = new ObjectInputStream(in);
				){
			
			// 파일에 저장할 데이터 객체 생성
			Member m1 = new Member("root", "root1234");
			Member m2 = new Member("quest", "quest");
			Member m3 = new Member("admin", "admin123456");
			
			oout.writeObject(m1);
			oout.writeObject(m2);
			oout.writeObject(m3);
			
			// 파일에서 Object 타입으로 읽기
			Member dataRead;
			dataRead = (Member)oin.readObject();
			dataRead.displayInfo();
			dataRead = (Member)oin.readObject();
			dataRead.displayInfo();
			dataRead = (Member)oin.readObject();
			dataRead.displayInfo();
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		
		
		System.out.println("\n프로그램 종료");
		
	} // end main()

} // end class
package com.lec.java.file08;

import java.io.Serializable;

public class Member implements Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = -4564811082080172359L;
	private String id;
	private String pw;
	transient private int num;
	transient private boolean isExist;
	
	// transient로 선언된 변수는 serialization(직렬화) 대상에서 제외됨.
	// (파일에 write되지 않는다)
	// de-serializtion(역직렬화, 파일에서 읽기)를 할 때는 
	// 해당 타입의 기본값(0, false, null)으로 초기화됨
	
	public Member() {}
	public Member(String id, String pw) {
		this.id = id;
		this.pw = pw;
		this.num = 123;
		this.isExist = true;
	}
	
	public void displayInfo() {
		System.out.println("--- 회원 정보 ---");
		System.out.println("아이디: " + id);
		System.out.println("비밀번호: " + pw);
		System.out.println("번호: " + num);
		System.out.println("Exist? " + isExist);
	}
	
} // end class Member 

Object Filter Stream + Collection

 Program <=== ObjectInputStream <=== FileInputStream <=== File
 Program ===> ObjectOutputStream ===> FileOutputStream ===> File

 ArrayList<> 와 같은 Collection 에서,
 모든 데이터들이 Serializable 되어 있으면 ObjectInputStream / ObjectOutputStream 으로 read/write 가능.

 

package com.lec.java.file09;

import java.io.EOFException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;

// Object Filter Stream + Collection

// Program <=== ObjectInputStream <=== FileInputStream <=== File
// Program ===> ObjectOutputStream ===> FileOutputStream ===> File

// ArrayList<> 와 같은 Collection 에서,
// 모든 데이터들이 Serializable 되어 있으면 ObjectInputStream / ObjectOutputStream 으로
// read/write 가능.

public class File09Main {
	
	public static final String FILEPATH  = "temp/member2.dat";

	public static void main(String[] args) {
		System.out.println("Object Filter Stream");

		try(
				OutputStream out = new FileOutputStream(FILEPATH);
				ObjectOutputStream oout = new ObjectOutputStream(out);
				InputStream in = new FileInputStream(FILEPATH);
				ObjectInputStream oin = new ObjectInputStream(in);
				){
			
			// 리스트 준비
			ArrayList<Member> list = new ArrayList<Member>();
			
			// 파일에 저장할 데이터 객체 생성
			Member m1 = new Member("root", "root1234");
			Member m2 = new Member("quest", "quest");
			Member m3 = new Member("admin", "admin123456");
			
			list.add(m1);
			list.add(m2);
			list.add(m3);
			
			oout.writeObject(list);  // List  를 한번에 저장

			list = null;
			
			list = (ArrayList<Member>)oin.readObject();
			for(Member m : list) {
				m.displayInfo();
			}
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		
		
		System.out.println("\n프로그램 종료");
		
	} // end main()

} // end class File08Main
package com.lec.java.file09;

import java.io.Serializable;

public class Member implements Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = -362426577793734250L;
	private String id;
	private String pw;
	transient private int num;
	transient private boolean isExist;
	// transient로 선언된 변수는 serialization(직렬화) 대상에서 제외됨.
	// (파일에 write되지 않는다)
	// de-serializtion(역직렬화, 파일에서 읽기)를 할 때는 
	// 해당 타입의 기본값(0, false, null)으로 초기화됨
	
	public Member() {}
	public Member(String id, String pw) {
		this.id = id;
		this.pw = pw;
		this.num = 123;    // de-serializtion 동작 확인을 위해 기본값 초기화
		this.isExist = true;
	}
	
	public void displayInfo() {
		System.out.println("--- 회원 정보 ---");
		System.out.println("아이디: " + id);
		System.out.println("비밀번호: " + pw);
		System.out.println("번호: " + num);
		System.out.println("Exist? " + isExist);
	}
	
	
} // end class Member 

문자(character) 단위 입출력 스트림 클래스
 java.io.Reader : 프로그램이 '문자 단위' 데이터를 읽어들이는(read) 통로
  └─ java.io.InputStreamReader
      └─ java.io.FileReader

 java.io.Writer : 프로그램이 '문자 단위' 데이터를 쓰는(write) 통로
  └─ java.io.OutputStreamWriter
      └─ java.io.FileWriter

 FileReader / FileWriter 객체는 '텍스트파일, 즉 문자 단위' 데이터를 읽고/쓰기 할때 사용하는 기반 스트립 입니다.  

 따라서 텍스트가 아닌 오디오, 비디오, 등의 파일을 다룰수 없습니다.
 주로 String 이나 char [] 내용을 입/출력 할때 사용합니다.

 텍스트 파일 (Text File) 이란
   사람이 읽을수 있는 글자들이 저장된 파일
   암호화 되지 않은 평범한 텍스트

 이진파일 (Binary File) 이란
   사람이 직접 읽을수는 없음.

   ★ 문자기반 출력시 꼭 끝에 flush() 해주자 ★

package com.lec.java.file11;

import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;

/*
문자(character) 단위 입출력 스트림 클래스
 java.io.Reader: 프로그램이 '문자 단위' 데이터를 읽어들이는(read) 통로
  └─ java.io.InputStreamReader
      └─ java.io.FileReader

 java.io.Writer: 프로그램이 '문자 단위' 데이터를 쓰는(write) 통로
  └─ java.io.OutputStreamWriter
      └─ java.io.FileWriter

 FileReader / FileWriter 객체는 '텍스트파일, 즉 문자 단위' 데이터를 읽고/쓰기 할때
 사용하는 기반 스트립 입니다.   따라서 텍스트가 아닌 오디오, 비디오, 등의 파일을 다룰수 없습니다.
 주로 String 이나 char [] 내용을 입/출력 할때 사용합니다.

 텍스트 파일 (Text File) 이란
   사람이 읽을수 있는 글자들이 저장된 파일
   암호화 되지 않은 평범한 텍스트

 이진파일 (Binary File) 이란
   사람이 직접 읽을수는 없슴.

   ★ 문자기반 출력시 꼭 끝에 flush() 해주자 ★
*/


public class File11Main {
	public static void main(String[] args) {
		System.out.println("FileReader / FileWriter");
		
		String src = "temp/FileData.txt";
		String dst = "temp/FileData.txt";
		
//		FileWriter fw = null;
//		FileReader fr = null;
		
		try(
				FileWriter fw = new FileWriter(dst);
				FileReader fr = new FileReader(src);				
				
				
				// byte 스트림 방식과 비교
				OutputStream out = new FileOutputStream("temp/FileData.bin");
				DataOutputStream dout = new DataOutputStream(out);
				) {
			
			String str = "안녕하세요";   // 한글 5글자
			char [] charArr = {'J', 'A', 'V', 'A'};  // 알파벳 4글자
			
			// Writer 는 주로 문자열(String) 이나 char [] 의 내용을 출력할때 사용
			
			fw.write(str);    // 저장은 시스템 인코딩 상태에 따라 저장됨.
			fw.write(charArr);  // UTF-8 인코딩의 경우 한글 한글자당 3byte, 영어는 한글자당 1byte
			
			fw.flush();  // flush() 메소드로 출력버퍼의 데이터를 완전히 출력
			
			// 읽기 read
			char [] buff = new char[100];   // 읽어들일 버퍼 준비
			
			int charsRead = fr.read(buff);   // 최대 buff 만큼의 텍스트 읽어들임. 읽은 문자개수 리턴
			
			for(int i = 0; i < charsRead; i++) {
				System.out.print(buff[i]);
			}
			System.out.println();
			System.out.println("읽은 문자 개수: " + charsRead);
			
			
			// byte 스트림으로 저장하기
			dout.writeChars(str);  
			for(char ch : charArr) {
				dout.writeChar(ch);
			}
						
		} catch (IOException e) {
			e.printStackTrace();
		}
//		finally {
//			try {
//				if(fw != null) fw.close();
//				if(fr != null) fr.close();
//			} catch (IOException e) {
//				e.printStackTrace();
//			}
//		}
		
		
		
		System.out.println("\n프로그램 종료");
		
	} // end main()
} // end class

버퍼사용 문자입출력 : BufferedReader, BufferedWriter
 
  java.lang.Object
  └─ java.io.Reader
      └─ java.io.BufferedReader
         
  java.lang.Object
   └─ java.io.Writer
       └─ java.io.BufferedWriter
        
  ★ 문자기반 출력시 꼭 끝에 flush() 해주자 ★     

package com.lec.java.file12;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

/*
 * 버퍼사용 문자입출력 : BufferedReader, BufferedWriter
 * 
 * java.lang.Object
 *  └─ java.io.Reader
 *      └─ java.io.BufferedReader
 *       
 * java.lang.Object
 *  └─ java.io.Writer
 *      └─ java.io.BufferedWriter
 *      
 * ★ 문자기반 출력시 꼭 끝에 flush() 해주자 ★     
 *             
*/

/*
 * txt 는 utf-8 로 인코딩 , 영문 텍스트
 */
public class File12Main {
	
	private static final String BIG_TEXT = "temp/big_eng.txt"; 
	
	public static void main(String[] args) {
		System.out.println("FileReader / FileWriter");
		
		try(
				FileReader fr = new FileReader(BIG_TEXT);
				FileWriter fw = new FileWriter("temp/big_eng_copy1.txt");	
				
				
				FileReader fr2 = new FileReader(BIG_TEXT);
				FileWriter fw2 = new FileWriter("temp/big_eng_copy2.txt");
				BufferedReader br = new BufferedReader(fr2);  // 장착!
				BufferedWriter bw = new BufferedWriter(fw2);
				){
			System.out.println("FileReader/FileWriter 만 사용");
			
			int charRead = 0;
			int charsCopied = 0;
			
			long startTime = System.currentTimeMillis(); 
//			while(true) {
//				charRead = fr.read();
//				if(charRead == -1) break;
//				fw.write(charRead);
//				charsCopied++;
//			}
			
			while((charRead = fr.read()) != -1) {
				fw.write(charRead);
				charsCopied++;
			}
			
			fw.flush();
			long endTime = System.currentTimeMillis(); 
			long elapsedTime = endTime - startTime;
			
			System.out.println("읽고 쓴 문자수: " + charsCopied);
			System.out.println("경과시간(ms): " + elapsedTime);
			
			
			System.out.println();
			System.out.println("BufferedReader/Writer + 버퍼 사용");
			
			char [] buf = new char[1024];  //  버퍼 제공
			
			int charsRead = 0;
			charsCopied = 0;
			
			startTime = System.currentTimeMillis(); 
			
			while((charsRead = br.read(buf)) != -1) {
				bw.write(buf, 0, charsRead);
				charsCopied += charsRead;
			}
			
			fw.flush();
			endTime = System.currentTimeMillis(); 
			elapsedTime = endTime - startTime;
			
			System.out.println("읽고 쓴 문자수: " + charsCopied);
			System.out.println("경과시간(ms): " + elapsedTime);
			
			
			
			
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		System.out.println("\n프로그램 종료");		
		
	} // end main()
} // end class

PrintWriter / 인코딩 
 
 java.lang.Object
  └─ java.io.Writer
      └─ java.io.PrintWriter
  
  텍스트 파일 작성시 PrintWriter 객체 편리
   println(), print(), printf() ..
  텍스트 파일 읽을시는 BufferedReader 객체 편리
   read(), read(버퍼), readLine()..
  
  PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("파일명" 혹은 File)));
  PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream("파일명" 혹은 File))));
  
  BufferedReader br = new BufferedReader(new FileReader("파일이름" 혹은 File));
  BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(new File("파일이름" 혹은 File))));
  BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("파일이름" 혹은 File)));
  
  ★ 문자기반 출력시 꼭 끝에 flush() 해주자 ★
 
  인코딩 문제 
   FIleReader, FileWriter 는 파일의 인코딩을 무조건 file.encoding 값으로 간주한다.
  (ex: LINUX 는  UTF-8,  MacOS 는 한글상위의 경우 euc-kr, 윈도우즈는 Java 소스코드 인코딩 영향) 
  
  인코딩 설정하기
   InputStreamReader, OutputStreamWriter 를 사용해서 인코딩 변경을 해야 한다.
  
   BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("파일이름" 혹은 File),"인코딩"));
   BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("파일이름" 혹은 File), "인코딩"));
   
  인코딩 : "UTF8", "euc-kr"

package com.lec.java.file13;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
public class File13Main {
	
	private static final String FILE1 = "temp/PrintData.txt";
	private static final String FILE2 = "temp/PrintData_euckr.txt";
	
	public static void main(String[] args) {
		System.out.println("PrintWriter / 인코딩 ");
		
		FileWriter fw = null;
		FileReader fr = null;
		
		PrintWriter out = null;
		
		BufferedReader br = null;
		BufferedWriter bw = null;
		
		try {
//			fw = new FileWriter(FILE1);
//			bw = new BufferedWriter(fw);
//			out = new PrintWriter(bw);
			
			out = new PrintWriter(new BufferedWriter(new FileWriter(FILE1)));
			
			test_write(out);
			
			System.out.println();
			
			br = new BufferedReader(new FileReader(FILE1));
			
			test_read(br);
			
			out.close();
			br.close();
			
			System.out.println("현재 인코딩 " + System.getProperty("file.encoding"));
			
			// euc-kr 로 인코딩하여 저장
			out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(FILE2), "euc-kr")));
			
			test_write(out);
			
			System.out.println();
			
			// 다른 인코딩으로 읽어오면 텍스트 깨진다
//			br = new BufferedReader(new InputStreamReader(new FileInputStream(FILE2)));
			
			// 원하는 인코딩으로 텍스트 읽기
			br = new BufferedReader(new InputStreamReader(new FileInputStream(FILE2), "euc-kr"));
			
			test_read(br);
			
			
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			out.close();
			
			try {
				if(br != null) br.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		
		
		System.out.println("\n프로그램 종료");
		
	} // end main()

	private static void test_read(BufferedReader br) throws IOException {
		String line;
		StringBuffer sb = new StringBuffer();
		while((line = br.readLine()) != null) {
			sb.append(line + "\n");
		}
		System.out.println(sb);
		
	}

	private static void test_write(PrintWriter out) {
		out.println("안녕하세요 Java 한글이 잘 보이나요?");
		out.print((2000 + 20) + " " + 3.14);
		out.println();
		out.printf("%d-%d-%d\n", 2020, 3, 2);
		out.flush();
		
	}
	
	// TODO
	
} // end class