*바이트 기반 스트림
바이트 스트림: 1byte 단위로만 입출력할 수 있는 좁은 통로(XXXInputStream / XXXOutputStream)
기반 스트림: 외부 매체와 직접적으로 연결되는 메인 통로
=> 외부 매체를 지정하고 그 외부매체와 직접적으로 연결되는 1byte 단위의 통로를 만들겠음
XXXInputStream: XXX 매체로부터 데이터를 입력받는 통로
(외부 매체로부터 데이터를 가지고 오겠다. == 읽어들이겠다)
XXXOutputStream: XXX 매체로부터 데이터를 출력하는 통로
(외부 매체에 데이터를 내보내겠다 == 쓰겠다)
DAO(Data Access Object)
: 데이터가 보관되어 있는 공간(외부 매체)과 직접 접근해서 데이터를 입출력하는 용도의 클래스
기능에만 충실한 패키지: controller
데이터와 관련된 패키지: model
외부 데이터와 직접 연결할 패키지: dao
프로그램 ---> 외부매체(파일)
출력: 프로그램 내의 데이터를 파일로 내보내기 (즉, 파일에 기록하겠다. 파일로 저장하겠다.)
package com.kh.chap02.byte_.model.dao;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileByteDao {
public void fileSave() {
// FileOutputStream: "파일"로 데이터를 1byte 단위로 출력하는 스트림
FileOutputStream fout = null; // 0단계! 변수 선언 및 null 초기화
try {
// 1. FileOutputStream 객체 생성 (== 연결 통로 만들기)
// => 해당 파일과 직접 연결되는 통로를 만들겠음(파일명을 매개변수로 생성자 호출)
// 해당 파일이 존재하지 않는다면 해당 파일이 생성되면서 연결 통로가 지어짐
// 존재하는 파일이라면 연결 통로만 바로 지어짐
fout = new FileOutputStream("a_byte.txt"/*, false */); // a_byte.txt가 없으니까 오류 발생할 수 있다며 계속 빨간줄 뜸
// => 기존에 해당 파일이 있을 경우 덮어씌워짐
// => 이어쓰고 싶을 경우에는? 매개변수로 true를 같이 넘겨 주면 됨 (단, 기본값은 false임)
// 2. 연결 통로로 데이터를 출력: write() 메소드 사용
// => 1byte 범위만 전송 가능: -128 ~ 127까지의 숫자, 음수는 불가!(아스키코드 값에 음수 존재하지 않음)
// (단, 파일에 기록되는 건 해당 숫자의 고유한 문자임: 아스키코드)
fout.write(97); // 'a'가 기록
fout.write('b'); // 'b'가 기록
// fout.write('김'); // 1byte짜리 통로에 2byte짜리의 한글이 들어가서 @라는 형태로 깨져서 출력됨!
// (바이트 스트림으로는 제한적)
byte[] bArr = {99, 100, 101}; // 'c', 'd', 'e'가 기록
fout.write(bArr);
fout.write(bArr, 1, 2); // 'd', 'e'가 기록
// 3. 스트림을 다 이용했으면 자원 반납하기 (반드시)
// => 즉, 연결통로를 끊겠음
// fout.close(); // 위에서 혹시 예외가 발생했을 경우 이 자리는 실행이 안 될 수도 있음! => finally 블록으로 위치 바꿔 줌
} catch(FileNotFoundException e) { // try ~ catch 블럭으로 처리해 주기, 존재하지 않는 경로를 제시했을 때
} catch (IOException e) { // 입출력 상황에서 어느 오류든지 발생했을 때
e.printStackTrace();
} finally { // 어떤 예외가 발생하든지 간에 반드시 실행할 구문을 작성하는 블럭
// 3. 스트림을 다 이용했으면 자원 반납하기 (반드시)
try {
fout.close();
} catch(IOException e) {
e.printStackTrace();
}
}
}
프로그램 <--- 외부매체(파일)
입력: 파일로부터 데이터를 가지고 오겠다 (즉, 읽어들이겠다)
public void fileRead() {
// FileInputStream: 파일로부터 데이터를 1byte 단위로 입력받는 스트림
// 0. 변수 선언 후 null로 초기화
FileInputStream fin = null;
try {
// 1. 스트림 객체 생성하기 == 연결 통로 만들기
fin = new FileInputStream("a_byte.txt"); // try ~ catch 블록에 넣기
// 파일 경로 제시, 입력받을 때에는 반드시 존재하는 파일명으로 제시해야 함!
// 2. 읽어들이기 == 입력받기: read() 메소드 사용
// => 단, 1byte 단위로 하나씩 읽어 옴
/*
System.out.println(fin.read()); // IOEcepton catch 블럭 추가하기
System.out.println(fin.read());
System.out.println(fin.read());
System.out.println(fin.read());
System.out.println(fin.read());
System.out.println(fin.read());
System.out.println(fin.read());
// 파일 끝을 만나는 순간 fin.read() => -1 반환
System.out.println(fin.read());
System.out.println(fin.read());
// 출력할 때는 문자로, 입력받을 때는 숫자로 받아짐
// 입력 시 (char)로 강제 형변환 하여 문자로 입력받을 수 있음
// println이 아닌 print로 연이어서 출력도 가능
*/
// 반복문으로 활용하기
// 퐁당퐁당 출력됨
/* 메소드를 한 번 돌릴 때마다 두 번 호출되는 구조라서 내가 원하는 값을 얻을 수 없음
while(fin.read() != -1) { // fin.read()의 결과값이 -1이 아닐 경우에만 반복 돌리겠음
System.out.println(fin.read());
}
*/
// 해결방법 1. 무한 반복으로 매번 조건 검사 하기
/*
while (true) {
int value = fin.read();
if(value == -1) {
break;
}
System.out.println(value);
}
*/
// 해결방법 2. 조건식 내부에 변수 대입 구문을 활용하는 방법 (권장)
int value = 0;
while((value = fin.read()) != -1) {
System.out.println(value);
}
// 3. 연결 통로 끊기 == 자원 반납하기 (무조건) => finally 블럭에 작성
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 3. 다 쓴 스트림 객체 반납
try {
fin.close(); // try ~ catch 블록으로 묶기
} catch (IOException e) {
e.printStackTrace();
}
} // 이제 실행 클래스에 fileRead(); 하러 감
}
}
// 출력물
97
98
99
100
101
100
101
*문자 기반 스트림
문자 스트림: 한 번에 2byte 짜리의 데이터가 이동할 수 있는 넓은 통로 (XXXReader / XXXwriter)
기반 스트림: 외부 매체와 직접적으로 연결되는 main 통로
=> 외부 매체를 지정하고 그 외부 매체와 직접적으로 연결되는 2byte짜리 넓은 통로를 만들겠음
프로그램 ---> 외부 매체(파일)
출력
package com.kh.chap03.char_.model.dao;
import java.io.FileWriter;
import java.io.IOException;
public class FileCharDao {
public void fileSave() {
// FileWriter: 파일로 데이터를 2byte 단위로 출력하는 스트림
// 0. 변수 선언 및 null로 초기화
FileWriter fw = null; // import
// 1. 스트림 객체 생성 == 연결 통로를 만들겠음 (파일명 제시)
try {
fw = new FileWriter("b_char.txt"); // 파일명 제시 이유: 누구랑 연결 통로를 만들 건지 알려 주기 위해서!
// 출력일 경우, 현재 존재하지 않는 파일명을 제시하더라도 파일 생성 후 연결 통로가 지어짐
// 2. 출력 == 데이터 내보내기: write() 메소드 사용
fw.write("와! IO 재미있다...ㅎ"); // 내부적으로 문자 한 개씩 끊어서 이동함
fw.write('A');
fw.write(97);
fw.write("\n"); // 개행문자도 가능
char[] cArr = {'k', 'i', 'w', 'i'};
fw.write(cArr);
// 3. 연결 통로 끊기 == 자원 반납(반드시)
// => finally 블록에 작성(finally 블록을 추가하면 지역 변수 이슈가 생겨 0단계 거쳐야 함)
} catch (IOException e) {
e.printStackTrace();
} finally {
// 3. 다 쓴 자원 반납하기 (반드시)
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// b_char.txt에 입력된 것
와! IO 재미있다...ㅎAa
kiwi
그럼 문자 스트림에서는 전체 문자열이 함께 이동하는 것일까?
아니여!
편의상 그렇게 보이는 것이지 내부적으로는 문자 한 개씩 끊어서 이동함
프로그램 <--- 외부 매체(파일)
입력
public void fileRead() {
// FileReader: 파일로부터 데이터를 2byte 단위로 입력받는 스트림
// 0. 변수 선언 및 초기화
FileReader fr = null; // import
// 1. 스트림 객체 생성 == 연결 통로를 짓겠음
try {
fr = new FileReader("b_char.txt"); // 입력 통로의 경우 무조건! 존재하는 경로로 제시해야 됨!
// 2. 입력받기: read() 메소드 사용
/*
System.out.println(fr.read());
System.out.println(fr.read());
System.out.println(fr.read());
System.out.println(fr.read());
System.out.println(fr.read());
System.out.println(fr.read());
System.out.println(fr.read());
System.out.println(fr.read());
System.out.println(fr.read());
System.out.println(fr.read());
System.out.println(fr.read());
System.out.println(fr.read());
System.out.println(fr.read());
System.out.println(fr.read());
System.out.println(fr.read());
System.out.println(fr.read());
System.out.println(fr.read());
System.out.println(fr.read());
System.out.println(fr.read());
System.out.println(fr.read());
System.out.println(fr.read());
// 파일 끝을 만나는 순간 fr.read() => -1 반환
System.out.println(fr.read());
*/
// => 문자 기반 스트림도 마찬가지로 문서의 끝을 만났을 때 read 메소드의 반환값이 -1임!!
// 반복문 활용
/*
while(fr.read() != -1) {
System.out.println(fr.read());
}
// => 반복이 한 번 일어날 때마다 read() 메소드가 2번 호출되기 때문에 2, 4번째 등 퐁당퐁당으로 출력됨
*/
int value = 0;
while((value = fr.read()) != -1) { // 소괄호로 우선순위 높이기!
System.out.print((char)value);
}
// 3. 연결 통로 끊기 == 자원 반납 (반드시)
} catch (FileNotFoundException e) {
} catch (IOException e) {
e.printStackTrace();
} finally {
// 3. 자원 반납 (반드시)
try {
fr.close();
} catch(IOException e) {
e.printStackTrace();
}
}
}
입력도 마찬가지로 한 글자씩 이동함
문자 하나씩 옮기는 거 너무 시간 오래 걸리지 않아?
나중에 데이터가 아~주 많아진다면
이렇게 중간에 모두 모아놨다가 한 번에 옮기는 게 더 빠르지 않을까?
↖ 버퍼(buffer)
이러한 기능을 할 수 있게 하는 게 보조 스트림
*보조 스트림
기반 스트림(외부 매체와 직접 연결되어 있는 통로)의 부족한 기능들을 확장시킬 수 있는 스트림
보조 스트림은 단독으로 사용 불가(즉, 단독으로 객체 생성이 불가)
=> 입력용 기반 스트림일 경우에는 입력용 보조 스트림을 써야 하고
출력용 기반 스트림일 경우에는 출력용 보조 스트림을 써야 함!
[ 표현법 ]
// 1단계: 기반스트림 생성
기반스트림클래스명 기반스트림객체명 = new 기반스트림클래스명(파일명);
// 2단계: 기반스트림객체를 매개변수 삼아서 보조스트림객체 생성
보조스트림클래스명 보조스트림객체명 = new 보조스트림클래스명(기반스트림객체);
=> 속도 성능 향상 목적의 보조스트림: BufferedXXX
(버퍼 공간을 제공해서 한 번에 내용물을 모아뒀다가 한꺼번에 입출력 진행)
프로그램 ---> 외부 매체(파일)
package com.kh.chap04.assist.part01.buffered.model.dao;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class BufferedDao {
// BufferedWriter (출력용) / BufferedReader (입력용)
public void fileSave() {
// FileWriter: 2byte 단위로 파일에 데이터를 내보내는 기반 스트림
// +
// BufferedWriter: 버퍼라는 공간을 제공해 주는 보조 스트림(속도 향상)
FileWriter fw = null;
BufferedWriter bw = null;
try {
// 1. 기반스트림 객체 생성(메인 연결 통로를 만들겠음)
fw = new FileWriter("c_buffer.txt"); // import, 지역변수 이슈로 변수 선언 및 초기화 하고 오기! try ~ catch
// 2. 보조 스트림 객체 생성 (매개변수로 기반스트림객체를 제시)
bw = new BufferedWriter(fw); // import
// 3. 출력: BufferedWriter 객체에서 제공하는 write() 메소드 사용
// fw.write(); // FileWriter 객체에서 제공하는 write() 메소드를 사용하면 성능 향상을 기대할 수 없음
bw.write("안녕하세요.");
bw.newLine(); // 개행을 넣어 주는 메소드
bw.write("반갑습니다.\n");
bw.write("저리가세요.");
// 4. 자원 반납 (반드시) => finally 블록 안에 작성
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4. 자원 반납 (반드시)
// => 주의할 점: 반납해야 할 자원이 2개(fw, bw)
// => 반납 순서: 객체 생성 순서의 "역순"으로 반납!
// 즉, 생성 순서가 fw -> bw이므로, 반납 순서는 bw -> fw
try {
bw.close(); // bw cannot be resolved: 지역변수 이슈이므로 0단계 함께 진행해 줘야 함!
// 변수 생성 후에는 Unhandled exception type IOException로 오류 메시지 바뀜! => try ~ catch문으로 처리
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
FileWriter에 대한 객체 생성을 따로 하지 않고 코드 한줄로 진행하는 법
// FileWriter fw = null;
// BufferedWriter bw = null;
// 0단계 변수 선언 및 초기화를 코드 한줄로 줄여서 표현
BufferedWriter bw = null;
.
.
.
// 1. 기반스트림 객체 생성(메인 연결 통로를 만들겠음)
// fw = new FileWriter("c_buffer.txt"); // import, 지역변수 이슈로 변수 선언 및 초기화 하고 오기! try ~ catch
// 2. 보조 스트림 객체 생성 (매개변수로 기반스트림객체를 제시)
// bw = new BufferedWriter(fw); // import
// 1, 2단계 기반 스트림 객체, 보조 스트림 객체 생성을 코드 한줄로 줄여서 표현
bw = new BufferedWriter(new FileWriter("c_buffer.txt"));
.
.
.
try {
bw.close(); // bw cannot be resolved: 지역변수 이슈이므로 0단계 함께 진행해 줘야 함!
// 변수 생성 후에는 Unhandled exception type IOException로 오류 메시지 바뀜! => try ~ catch문으로 처리
// fw.close(); // 코드를 한줄로 줄이면 얘는 필요하지 않게 됨
} catch (IOException e) {
e.printStackTrace();
}
프로그램 <--- 외부 매체(파일)
(FileWriter에 대한 객체 생성을 따로 하지 않고 코드 한줄로 진행)
public void fileRead() {
// FileReader: 파일과 직접적으로 연결해서 한번에 2byte 단위로 데이터를 입력할 수 있는 기반 스트림
// +
// BufferedReader: 버퍼라는 공간을 제공해 주는 보조 스트림 (속도 향상)
// 0. 변수 선언 및 null 값으로 초기화
BufferedReader br = null;
try {
// 1. 객체 생성 == 통로를 만들겠다
br = new BufferedReader(new FileReader("c_buffer.txt"));
// 2. 입력: BufferedReader 클래스에서 제공하는 메소드로 읽어들이기
/*
System.out.println(br.readLine()); // 줄 단위로 내용물을 읽어들임
System.out.println(br.readLine());
System.out.println(br.readLine());
System.out.println(br.readLine()); // 문서의 끝을 만났을 때 null을 반환
*/
// 반복문 활용
String value = null;
// String value = ""; // 이렇게 초기화해도 무방함
while((value = br.readLine()) != null) {
System.out.println(value);
}
// 3. 연결 통로를 끊겠다 == 자원 반납(반드시) => finally 블럭에 작성
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 3. 자원 반납 (반드시)
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 출력물
안녕하세요.
반갑습니다.
저리가세요.
// null // 반복문 활용 시 null 값은 안 나옴
*코드를 더 줄이고 싶다면?
=> try ~ with ~ resource 구문 (jdk7 버전 이상부터만 가능)
[ 표현법 ]
try(기반/보조 스트림 객체 생성) {
예외가발생할법한구문;
} catch(예외클래스명 e) {
해당예외발생시실행할구문;
}
=> 스트림 객체 생성 구문을 try(여기) 에 작성하게 되면
스트림 객체 생성 후 해당 try 블럭 내용이 실행된 후 알아서 자원 반납이 됨
try(BufferedReader br = new BufferedReader(new FileReader("c_buffer.txt"))){
// 반복문 활용
String value = null;
while((value = br.readLine()) != null) {
System.out.println(value);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// 출력물
안녕하세요.
반갑습니다.
저리가세요.