본문 바로가기

웹개발/JAVA

[JAVA] I/O Stream

 

Stream : Data의 흐름(통로)

 

 

- [도착지]                                             [출발지]

   Program <--- InputStream <--- Source(Keyboard, Mouse, File, Network)

- [출발지]                                             [도착지]

   Program ---> OutputStream ---> Destination(Monitor, Beam, File, Network)

 

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

  1. 바이트(byte) 단위 입출력 스트림 클래스
    • java.io.InputStream : 프로그램이 바이트 단위 데이터를 읽어들이는(read) 통로
      • java.io.FileInputeStream 
    • java.io.OutputStream : 프로그램이 바이트 단위 데이터를 쓰는(write) 통로
      • java.io.FileOutputStream
    • => 위 두개 클래스는 추상클래스
  2. 문자(character) 단위 입출력 스트림 클래스
    • java.io.Reader : 프로그램이 문자 단위 데이터를 읽어들이는 통로
    • java.io.Writer : 프로그램이 문자 단위 데이터를 쓰는 통로
  3. 파일 시스템의 파일정보를 얻기 위한 클래스 
    • java.io.File
  4. 콘솔로부터 문자를 입출력하기 위한 클래스
    • java.io.Console

1. 바이트(byte) 단위 입출력 스트림 클래스

FileInputStream, FileOutputStream

package 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;

public class File02Main {

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

		InputStream in = null;	// 바이트 단위로 데이터를 읽고 쓰는 클래스
		OutputStream out = null;
		
		try{
			in = new FileInputStream("temp/big_text.txt");	// 업캐스팅
			out = new FileOutputStream("temp/copy_big_text.txt");
            // 해당 파일이 없으면, 새로 생성.
			//   ..      있었으면, 지우고 새로 생성.
			
			int dataRead;
			
			// 파일 복사 - 한 byte씩 읽어서 복사한다(쓴다)
			while(true) {
				// 데이터 읽기: InputStream에 있는 read() 메소드 사용
				// read()는 InputStream 으로부터 
				// 1byte 씩 읽어서 int(4byte) 에 담아 리턴한다 - 그래서 dataRead의 자료형이 int
				// [ ... ][ ... ][ ... ][ 1byte ]
				dataRead = in.read();
				if(dataRead == -1)	break;	// 더이상 읽을 것이 없으면 read() 는 -1 을 리턴한다.
				
				// 데이터 쓰기: OutputStream에 있는 write() 메소드 사용
				// write() 는 
				// int(4byte)에 1byte씩 담아 OutputStream에 쓴다
				// [ ... ][ ... ][ ... ][ 1byte ]
				out.write(dataRead);
			}

		}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

- try - with - resource        :   Java 7 부터 도입

     =>  try(리소스 생성){ . . .}catch(exception ){ . . . }           :   리소스를 close하는 코드가 없어도 자동으로 close가 실행

     - 조건 : 

            java.lang.AutoCloseable 나 

                ㄴ>  java.io.Closeable 을 상속받은 객체

     - InputStream, OutputStream 둘 다 Closeable을 상속(Implements)한다.

package 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;

public class File03Main {

	public static void main(String[] args) {
		System.out.println("File IO 2");
		
        // try-with-resource
		try(
			InputStream in = new FileInputStream("temp1/big_text.txt");
			OutputStream out = new FileOutputStream("temp1/copy_big_text.txt");
			)
		{
			byte [] buff = new byte[1024];	//	버퍼 준비
			int lengthRead =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
			}

			long endTime = System.currentTimeMillis();
			long elapsedTime = endTime-startTime;
			
			System.out.println();
			System.out.println(elapsedTime); 
            // 입출력 스트림으로부터 한번에 1024 바이트씩 read/write 하니까 빨라짐
			
		}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

 Program <=== DataInputStream <=== FileInputStream <=== File

 Program ===> DataOutputStream ===> FileOutputStream ===> File

출처 : http://www.pskills.in/java/IO-stream-java.jsp

package 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;

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{
			in = new FileInputStream("temp1/big_text.txt");
			bin = new BufferedInputStream(in);	// 장착!
			out = new FileOutputStream("temp1/copy_big_text.txt");
			bout = new BufferedOutputStream(out);	// 장착!
			
			int dataRead;
			long startTime = System.currentTimeMillis();
			
			// 파일 복사
			while(true) {
				dataRead = bin.read();
				if(dataRead == -1)	break;	// 더이상 읽을 것이 없으면 read() 는 -1 을 리턴한다.
				
				// 데이터 쓰기: OutputStream에 있는 write() 메소드 사용
				bout.write(dataRead);
				bytesCopied++;
			}
			
			long endTime = System.currentTimeMillis();
			
			System.out.println(endTime - startTime);
            // Buffered I/O Stream 을 기존의 I/O Stream에 장착하면, 자체적으로 버퍼링하여, 입출력의
            // 속도가 향상
            // -> byte[] buff = new byte[1024](=buffer)가 했던 역할과 비슷하게 어느정도 데이터가 쌓일때까지 
            // 자체적으로 버퍼링 ==> 버퍼의 장점을 스트림에 적용

		}catch(FileNotFoundException e){
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			// 리소스 해제, 반납
			try {
				if(bout!=null) bout.close();	//  bout 을 close 하면 bout을 만든 out 도 자동 close 됨
				if(bin!=null) bin.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		System.out.println("\n프로그램 종료");
		
	} // end main()

} // end class
더보기

+ 여기에 버퍼까지 제공하면 비약적으로 I/O 속도 향상

- byte [] buff = new byte[1024];

- lengthRead = bin.read(buff);

- bout.write(buff, 0, lengthRead);

- Data filter stream

출처 : https://www.codingeek.com/java/io/data-streams-a-convinient-read-write-primitives-in-java/

package 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;


public class File06Main {

	public static void main(String[] args) {
		System.out.println("Data Filter Stream");
		
		try(
				OutputStream out = new FileOutputStream("temp1/data.bin");
				DataOutputStream dout = new DataOutputStream(out);
				InputStream in = new FileInputStream("temp1/data.bin");
				DataInputStream din= new DataInputStream(in);
				)
		{
			dout.writeBoolean(true);	//1byte
			dout.writeInt(100);	// 4byte
			dout.writeDouble(3.14);	// 8byte
			dout.writeChar('a');	// 2byte
			
			
			boolean b = din.readBoolean();
			System.out.println("boolean: "+b);
			
			System.out.println("int: "+din.readInt());
			
			System.out.println("double: "+din.readDouble());

			System.out.println("char: "+din.readChar());
			
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
		System.out.println("\n프로그램 종료");
		
	} // end main()

} // end class

 

Object Filter Stream 

-> 객체의 입출력을 위한 스트림

 Program <=== ObjectInputStream <=== FileInputStream <=== File

 Program ===> ObjectOutputStream ===> FileOutputStream ===> File

  • Object stream의 입출력 대상이 되는 클래스는 Serializable 인터페이스를 구현
  • 클래스의 일부 멤버 변수를 직렬화(Serialization)의 대상에서 제외시키려면 transient 키워드를 사용
// File08Main.java
package file08;

// import문 생략

public class File08Main {
	
	public static final String FILEPATH  = "temp1/member.dat";

	public static void main(String[] args) {
		System.out.println("Object Filter Stream");
		
		try( // try-with-resource
				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("guest", "guest");
			Member m3 = new Member("admin", "admin123456");
			
			oout.writeObject(m1);			// 저장하기
			oout.writeObject(m2);
			oout.writeObject(m3);
			
			Member dataRead;			// 읽어오기
			// EOFException 으로 끝까지 read한것을 체크
			// EOF : End Of File
			while(true) {
				dataRead = (Member)oin.readObject();
				dataRead.displayInfo();				
			}

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}catch(EOFException e) {
			e.printStackTrace();
		}catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		
		System.out.println("\n프로그램 종료");
		
	} // end main()

} // end class
// Member.java
package file08;

import java.io.Serializable;

public class Member implements Serializable{

	private static final long serialVersionUID = 3596187354707645991L;
	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

결과 : transient로 선언된 변수는 기본값(0, false, null)으로 초기화

 

더보기

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

- ArrayList<Member> list = new ArrayList<Member>();

- list.add(new Member("root", "root1234"));

- list.add(new Member("guest", "guest"));

- oout.wirteObject(list);

- list = null;

- list = (ArrayList<Member>)oin.readObject();

 

 

2. 문자(character) 단위 입출력 스트림 클래스

  • java.io.Reader
    • java.io.InputStreamReader
      • java.io.FileReader
  • java.io.Writer
    • java.io.OutputStreamWriter
      • java.io.FileWriter

[!] 문자기반 출력시에 꼭 끝에 flush() 해주기

 

package file11;

// import 생략

public class File11Main {
	public static void main(String[] args) {
		System.out.println("FileReader / FileWriter");
		
		String src = "temp1/FileData.txt";
		String dst = "temp1/FileData.txt";
		
		try(
				FileWriter fw = new FileWriter(dst);
				FileReader fr = new FileReader(src);
				){
			String str = "안녕하세요";
			char [] charArr = {'J','V','A','A'};
			// utf-8 인코딩의 경우 한글 한글자당 3바이트
			
			fw.write(str);
			fw.write(charArr);
			fw.flush();	// write() 메소드를 호출한 후에는 flush() 메소드로 출력버퍼의 데이터를 완전히 출력(비움)
			
			// 읽기(read)
			char [] buff = new char[100];
			int charsRead = 0; // 읽어드린 문자의 개수
			charsRead = fr.read(buff);
			for(int i=0;i<charsRead;i++) {
				System.out.print(buff[i]);
			}
			System.out.println();
			System.out.println("읽은 문자 개수 : "+ charsRead);
			
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		System.out.println("\n프로그램 종료");
		
	} // end main()
} // end class

- 버퍼사용 문자입출력(BufferedReader, BufferedWriter)

package file12;

// import 생략

/*
 * txt 는 utf-8 로 인코딩 , 영문 텍스트
 */
public class File12Main {
	
	private static final String BIG_TEXT = "temp1/big_eng.txt"; 
	
	public static void main(String[] args) {
		System.out.println("FileReader / FileWriter");
		
		FileWriter fw = null;
		FileReader fr = null;
		
		BufferedReader br = null;
		BufferedWriter bw = null;
		
		int charRead = 0;
		
		try {
			System.out.println("BufferedReader / Writer + 버퍼 사용");
			fr = new FileReader(BIG_TEXT);
			fw = new FileWriter("temp1/big_eng_1");
			br = new BufferedReader(fr); // 장착 
			bw = new BufferedWriter(fw); // 장착
			
			char [] buf = new char[1024];  //  버퍼 
			
			while((charRead = fr.read(buf))!= -1) {
				fw.write(buf, 0, charRead);
			}
		
		}catch(FileNotFoundException e) {
			e.printStackTrace();
		}catch (IOException e) {
			e.printStackTrace();
		}finally {
			
			try {
				if (br != null)
					br.close();
				if (bw != null)
					bw.close();
			} 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)));

package file13;

// import 생략
 *  인코딩 문제 
 *  	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), "인코딩"));
 *   
*/

public class File13Main {
	
	private static final String FILE1 = "temp1/PrintData.txt";
	private static final String FILE2 = "temp1/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);
			br = new BufferedReader(new FileReader(FILE1));
			
			test_read(br);
			
			System.out.println("현재 OS 인코딩 "+ System.getProperty("file.encoding"));
			
			// ecu-kr로 인코딩하여 저장
			out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(FILE2),"euckr")));
			test_write(out);
			
			br = new BufferedReader(new InputStreamReader(new FileInputStream(FILE2),"euckr"));
			test_read(br);
			
		}catch(FileNotFoundException e) {
			e.printStackTrace();
		}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",3);
		out.flush();
	}
	
} // end class

 

3. 파일 시스템의 파일 정보를 얻기 위한 클래스

// 데이터
private List<PhonebookModel> pbList = new ArrayList<>();
	
// 저장할 파일에 대한 변수
private static final String DATA_DIR = "data";
private static final String DATA_FILE = "phonebook.dat";
private File pbDir;
private File pbFile;
    
private void initFile() {
	// 1. 데이터 저장 폴더 생성(없었다면!)
	pbDir = new File(DATA_DIR);
	if(!pbDir.exists()) {
		if(pbDir.mkdir()) {
			System.out.println("폴더 생성 성공!");
		} else {
			System.out.println("폴더 생성 실패!");
		}
	}
				
	// 2. 데이터 저장 파일 생성(없었다면!)
		
	File pbFile = new File(pbDir, DATA_FILE);
	if(!pbFile.exists()) {
		System.out.println("데이터 파일 새로 생성");
	}else{
    	System.out.println("데이터 파일 존재 "+ pbFile.getAbsolutePath());
    }
	
    try(
			InputStream in = new FileInputStream(pbFile.getAbsolutePath());
			ObjectInputStream oin = new ObjectInputStream(in);
			) {
			
		PhonebookModel dataRead;
		// 파일의 내용을 읽어와서 --> pbList에 담기
		while(true) {
			dataRead = (PhonebookModel)oin.readObject();
			pbList.add(dataRead);
		}
        //ㄴ> pbList = (ArrayList<PhonebookModel>)oin.readObject(); 도 가능
	} catch (FileNotFoundException e) {
		e.printStackTrace();
	} catch(EOFException e) {
		System.out.println("데이터 로딩 완료!");
	}catch (IOException e) {
		e.printStackTrace();
	} catch (ClassNotFoundException e) {
		e.printStackTrace();
	} 
    
 @Override
public void close() throws IOException {
		// TODO : pbList -> 파일로 저장 
		OutputStream out = new FileOutputStream(pbFile);
		ObjectOutputStream oout = new ObjectOutputStream(out);
		
		for(PhonebookModel m : pbList) {
			oout.writeObject(m);
		}
		// ㄴ> oout.writeObject(pbList); 도 가능
		
}

'웹개발 > JAVA' 카테고리의 다른 글

싱글톤 클래스  (0) 2022.02.14
Java EE vs. Java SE  (0) 2021.10.31
[JAVA] Comparator<>, Comparable<>  (0) 2021.09.04
[JAVA] MAP - HashMap, TreeMap  (0) 2021.09.04
[JAVA] Collection - Set(HashSet, TreeSet)  (0) 2021.09.04