[Java] day22. 입출력IO / Stream / File
입출력이라는 것을 알아볼 때, 이 입력장치는 키보드가 될 수도 있고 프로그램이 될 수도 있지만,
우선 파일을 외부 자원으로 보고 확인하기 위해 파일 클래스에 대해 가장 먼저 학습했다.
File 클래스
파일 시스템의 파일을 표현하는 클래스
JDK 1.0부터 지원하는 API로 파일 처리를 수행하는 대표적인 클래스 이다.
대상 파일에 대한 정보로 인스턴스를 생성하고
파일의 생성, 삭제 등등의 처리를 수행하는 기능을 제공하고 있다.
파일 클래스를 이용해서 인스턴스를 생성한다.
대상 파일이 존재하지 않아도 인스턴스를 생성할 수 있다.
File file = new File("section01/file/test.txt";)
createNewFile()을 통해 파일을 생성할 수 있고 성공 실패 여부를 boolean으로 반환한다. (최초 실행시에만 true 이후에는 파일이 한번 생성되고 난 이후에 새롭게 파일을 만들지 않기 때문에 false를 반환한다.)
try {
boolean createSucess = file.creatNewFile();
System.out.prientln(createSuccess);
} catch(IOException) {
e.printStackTrace();
}
생성한 파일의 정보를 확인해 보자.
(file.length() + "byte"); //파일의 크기
(file.getPath()); //파일의 경로
(file.getParnts()); //현재 파일의 상위 경로
(file.getAbsolutePath()); //파일의 절대경로
파일의 절대 경로란 최상위 루트 위치부터의 경로를 의미한다.
그리고 당연하게도 파일을 지울 수도 있다.
삭제 후 성공 실패 여부를 boolean으로 반환한다.
boolean deleteSuccess = file.delete();
System.out.prientln(deleteSuccess);
이 외에도 파일에 대한 여러 메소드 기능이 제공된다.
입출력이란?
Input과 Output의 약자로 컴퓨터 내부 또는 외부 장치와 프로그램간의 데이터를 주고 받는 것.
장치와 입출력을 위해서는 하드웨어 장치에 직접 접근이 필요한데 다향한 매체에 존재하는 데이터들을
사용하기 위해 입출력 데이터를 처리할 공통적인 방법으로 스트림을 이용한다.
스트림 Stream 이란?
입출력 장치에서 데이터를 읽고 쓰기 위해서 자바에서 제공하는 클래스
모든 스트림은 단방향이며 각각의 장치마다 연결할 수 있는 스트림이 존재한다.
하나의 스트림으로 입출력을 동시에 수행할 수 없으므로 동시에 수행하려면 2개의 스트림이 필요하다.
구분 | 바이크 기반 스트림 | 문자 기반 스트림 | ||
입력 스트림 | 출력 스트림 | 입력 스트림 | 출력 스트림 | |
최상위 클래스 | InputStream | OutputStream | Reader | Writer |
하위 클래스 | XXXInputStream | XXXOutputStream | XXXReader | XXXWriter |
입출력과 관련된 API는 java.io 패키지에서 제공하고 있다.
API 문서에서 목록을 확인해보면 InputStream/OutputStream, Reader/Wtriter로 끝난다.
이 중 InputStream과 Reader는 데이터를 읽어오는 입력스트림이고,
OutputStream과 Writer는 데이터를 내보내는 출력 스트림이다.
자바 프로그램과 연결되는 외부 데이터 타입이 무엇인지는 클래스의 이름을 보고 유추가 사능하다.
예를들어 외부 데이터가 File이라면 클래스 이름은 FileInputStream이다. XXXInputStream 형태로 작성되있다.
1. 입력스트림
1-1. FileInputStream
(InputStream : 바이트 기반 입력 스트림의 최상위 클래스로 추상클래스)
대상 파일이 존재하지 않는 경우 발생하는 FileNotFoundException에 대해 핸들링 해야 한다.
인스턴스만 생성한 후 실행해보면 예외가 발생한 것을 볼 수 있다.
파일을 직접 생성한 뒤 다시 실행하면 예외가 발생하지 않는데 스트림 인스턴스가 정상적으로 생성된 것이다.
FileInputStream fin = null;
try {
fin = new FileInputStream("section02/stream/testInputStream.txt");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
read() 메소드를 사용하여 파일안의 값을 한번 출력해보자.
read()는 throws IOException이므로 catch 블럭을 추가.
read() 파일에 기록된 값을 1byte씩 순차적으로 읽어오고 더 이상 읽어올 데이터가 없는 경우 -1 반환
FileInputStream fin = null;
try {
fin = new FileInputStream("section02/stream/testInputStream.txt");
int value;
while((value = fin.read()) != -1){
System.out.println(value); //값은 정수로 읽어온다.
System.out.println((char)value); //문자로 출력하고 싶은 경우 형변환
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
한글 값을 입력하는 경우 한글이 깨져서 나온다.
한글은 한 글자의 3byte이기 때문에 1byte씩 읽어오면 글자가 깨지게 된다.
그렇기 때문에 1byte씩 읽어오는 경우 대부분의 경우 비효율적이다.
byte 배열을 이용해서 한 번에 읽어오는 방법도 제공한다.
length()를 이용해 fileSize를 구한 뒤 이 길이만큼의 byte 배열을 선언.
read() 메소드의 인자로 생성한 byte 배열을 넣어주면
파일의 내용을 읽어서 byte 배열에 넣어준다.
int fileSize = (int) new File ("section02/stream/testInputStream.txt").length();
byte[] bar = new byte[fileSize];
fin.read(bar);
for (int i=0 ; i < bar.length; i++){
System.out.println((char) bar[i]);
}
fin인스턴스가 null이 아닌 경우 자원을 반납해야 한다.
} Finally {
if(fin != null){
try {
fin.close();
} catch (IOException e) {
e.printStackTrace();
}
}
자원 해제를 하는 경우에도 IOException 핸들링 해야 한다.
이미 자원이 반납된 경우 발생하는 Exception이다.
자원을 반납해야 하는 경우
- 장기간 실행중인 프로그램에서 스트림을 닫지 않는 경우 다양한 리소스에 누수(leck)가 발생한다.
- 뒤에서 배우는 버퍼를 이용하는 경우 마지막에 flush()로 버퍼에 있는 데이터를 강제로 전송해야 한다.
만약 잔류 데이터가 남은 상황에서 추가로 스트림을 사용한다면 deadlock 상태가 된다.
판단하기 어렵고 의도하지 않은 상황에서도 이런 현상이 발생할 수 있기 때문에
마지막에는 fluch()를 무조건 실행해주는 것이 좋다.
close()메소드는 자원을 반납하여 fluch()를 해주기 때문에 close()만 제대로 해주어도 된다.
1-2. FileReader
(Reader : 문자 기반 입력 스트림의 최상위 클래스로 추상클래스)
FileInputStream과 사용하는 방법이 거의 동일하다.
단, byte 단위가 아닌 charactor 단위로 읽어들이는 부분이 차이점이다.
따라서 2바이트던 3바이트던 글자 단위로 읽어오기 때문에 한글을 정상적으로 읽어올 수 있다.
FileReader fr = null;
try {
fr = new FileReader("section02/stream/testReader.txt");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
testReader.txt
안녕하세요
파일 내용을 읽어오는 것도 동일하다.
FileReader fr = null;
try {
fr = new FileReader("section02/stream/testReader.txt");
int value;
while((value = fr.read()) != -1){
System.out.println((char) value);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
한글 값을 입력해도 하나의 글자 단위로 읽어온다. 안녕하세요가 잘 출력된다.
이것을 배열로 읽어들이는 방식도 동일하나,
byte 배열이 아닌 char 배열로 내용을 읽어오는 기능을 제공한다.
char[] carr = new char[(int) new file("section02/stream/testReader.txt").length()];
fr = read(carr);
for (int i=0; i < carr.length; i++){
System.out.println(carr[i]);
}
역시 동일하게 자원을 반납해주어야 한다.
} Finally {
if(fr != null){
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
2. 출력스트림
2-1.FileOutputStream
(OutputStream : 바이트 기반 출력 스트림의 최상위 클래스로 추상클래스)
프로그램의 데이터를 파일로 내보내기 위한 용도의 스트임이다.
1바이트 단위로 데이터를 처리한다.
FileOutputStream fout = null;
try{
fout = new FileOutputStream("sction02/stream/testOutputStream.txt");
}catch(FileNotFoundExcrption){
e.printStackTrace();
}
FileNotFoundExcrption을 핸들링 해야 하는 것은 동일하지만
실행해도 예외가 발생하지 않고 인스턴스가 잘 생성된다.
OutputStream의 경우 대상 파일이 존재하지 않으면 파일을 자동으로 생성해준다.
FileNotFoundExcrption이 핸들링 하는 부분은 경로이다 => 경로가 다를 경우 (지정된 경로를 찾을 수 없습니다) 발생
단순히 파일이 없는 경우는 자동으로 생성해준다.
두번째 인자로 true를 전달하면 이어쓰기가 된다.
전달하지 않으면 (false) 덮어쓰기가 된다. 위에 코드는 덮어쓰기가 된다.
FileOutputStream fout = null;
try{
fout = new FileOutputStream("sction02/stream/testOutputStream.txt", true);
fout.write(97);
}catch(FileNotFoundExcrption){
e.printStackTrace();
//white() 메소드도 역시 IOException을 핸들링 해야한다.
}catch(IOExcrption){
e.printStackTrace();
}
=> a가 기록이 됨. 한번더 실행하면 aa로 기록됨.
byte 배열을 이용해서 한 번에 기록할 수도 있다.
byte[] bar = new byte[] {98, 99, 100, 101, 102, 10}; //10 : 개행문자 (엔터) 이다.
fout.white(bar);
//bar의 1번 인덱스부터 3의 길이만큼 파일 내보내기
fout.white(bar, 1 , 3);
누수가 일어나지 않도록 finally로 닫아주기.
finally {
if(fout!=null){
try{
fout.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
2-1. FileWriter
(Writer : 문자 기반 출력스트림의 최상위 클래스로 추상클래스)
프로그램의 데이터를 파일로 내보내기 위한 용도의 스트림이다.
1글자 단위로 데이터를 처리한다.
대상 파일이 없으면 자동으로 생성해준다. 경로가 동일해야만 FileNotFoundException이 발생하지 않는다.
FileWriter fw = null;
try {
fw = new FileWriter("sction02/stream/testWriter.txt");
}catch(FileNotFoundExcrption){
e.printStackTrace();
}
두 번째 인자로 true를 전달하면 이어쓰기, 인자를 전달하지 않으면(false) 덮어쓰기가 된다.
FileWriter fw = null;
try {
fw = new FileWriter("sction02/stream/testWriter.txt" , true);
fw.write(97);
//fw,flush();
}catch(FileNotFoundExcrption){
e.printStackTrace();
}catch(IOException){
e.printStackTrace();
}finally{
if(fw !=null){
try{
fw.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
문자 단위 출력도 내부 버퍼를 사용하므로 쌓여있는 데이터를 flush()로 내보내줘야
최종적으로 파일에 출력되는 모습을 확인할 수 있다. 혹은 close()로 자원을 반납하면 반납 전에
flush()가 호출되므로 파일에 출력되는 모습을 확인할 수 있다.
문자 기반 스트림은 char 문자형 , char 배열, String 문자열 내보내기가 가능하다.
pw.write('A');
pw.write(new char[] {'a','p','p','l','e'});
pw.write("사과는 청송 사과")