자바에서 I/O 기능을 담당하는 대표적인 class가 있습니다.
위의 class들 중에 대표적으로 사용되는 것들을 알아보겠습니다.
1. byte 단위로 data를 교환하는 ByteStream
대표적인 ByteStream은 InputStream , OutputStream이 존재합니다.
Input ,Output 에서 유추할 수 있듯이
data가 나를 기준으로 들어오면 InputStream
data가 나를 기준으로 나가면 OutputStream 입니다.
2. 2byte 단위로 data를 읽어 문자로 변환해주는 Character Stream
Character Stream은 1byte 단위로 쓰일 수도 2byte단위로 쓰일 수도있습니다.
byte단위로 구성된 data를 사람이 볼 수 있도록 만들어주는 기능을 합니다.
영어만 쓰는 경우에는 byte단위로 읽어내도 문제가 없지만
한국어의 경우는 2byte씩 읽어야 하기 떄문에
문자 encoding 방식에 따라서 해석하는 형식이 달라집니다.
InputStream 의 종류
대표적으로 사용되는 ByteStream은 InputStream 입니다.
InpusStream의 SubType인 FileInputStream은 파일을 Byte단위로 읽어냅니다.
실제로 다음과 같이 declaration 돼있습니다.
class FileInputStream extends InputStream
하지만 앞서 말했듯이 한국어 같은 경우에는 2byte로 이루어져 있습니다.
한국어로 이루어진 txt파일의 경우 똑바로 읽어낸다고 해도 사람이 볼 수 없는 형태로 decoding 될 것 입니다.
이런 경우에 하나하나 체크해주면서 decoding을 해줘야 하는데 상당히 번거로운 작업입니다.
그래서 나온 것이 InputStreamReader 입니다.
public class InputStreamReader extends Reader
byte단위로 읽어내는 것은 똑같지만 CharacterSet을 갖고 있어
2byte가 필요한 문자의 경우 자동으로 묶어서 decoding 해주는 기능을 갖고 있습니다.
상당히 좋아보이지만 , 극복하지 못한 단점이 존재합니다.
InputStreamReader는 한 글자씩 읽어내기 때문에 기능적으로 상당히 느립니다.
이러한 단점을 극복하기 위해 BufferedReader 가 존재합니다.
public class BufferedReader extends Reader
똑같은 기능을 하지만 Buffer를 이용해 문자를 저장하여 문자열의 형태로 읽어냅니다.
따라서 상당히 효율적입니다.
OutputStream 의 종류
FileOutputStream은 InputStream과 마찬가지로 파일을 byte단위로 data를 내보냅니다.
대표적인 내장 메서드로 write()를 갖고있으며 Buffer 를 따로 갖고있지 않기 떄문에
별도의 동작없이 Write() 사용시 바로 data를 내보냅니다.
또한 byte단위로 내보내기 떄문에 한글파일을 보낼경우 깨지게 됩니다.
class FileOutputStream extends OutputStream
OutputStream에서 또한 InputStream과 비슷하게
OutputStreamWriter 를 가집니다.
public class OutputStreamWriter extends Writer
이 또한 동일하게 1byte 혹은 2byte 단위로 data를 내보내게 됩니다.
마찬가지로 Buffer를 가지고 한꺼번에 작업을 처리하는 class들을 갖고있습니다.
대표적으로 PrintWriter , BufferWriter 이 존재합니다.
public class PrintWriter extends Writer
public class BufferedWriter extends Writer
두 class 모두 buffer를 사용하기 떄문에 버퍼에 임시로 보관된 데이터를 언제 내보낼지 알려주는 flush() 메서드가 존재합니다.
이 전까지는 buffer에 data를 계속해서 넣다가 flush()를 만나면 버퍼를 비우면서 내보내게 되는 것 입니다.
아래는 간단한 사용법 입니다.
BufferedWriter bw = new BufferedWriter(new FileWriter(new File("src/main/resources/newFile.txt")));
String str = "새로 쓰는 문자열";
bw.write(str); // buffer 에 문자열 담기
bw.newLine(); // 줄 바꿈
bw.flush(); // buffer 비우기 , 실질적으로 파일에 문자열 쓰여짐
bw.close(); // BufferedWriter 닫기
PrintWriter는 AutoFlush 옵션을 갖고 있습니다.
이 옵션을 true로 설정하면 매 print 마다 flush를 호출합니다.
이는 편리하지만 지나치게 빈번한 flush() 메서드를 호출하게 되어
모아서 출력하는 버퍼의 효과가 사라지며 상당히 많은 시간을 소요하게 됩니다.
따라서 자동으로 출력버퍼를 비우도록 하는 것은 바람직하지 않습니다.
PrintWriter pw = new PrintWriter(new FileWriter(new File("src/main/resources/newFile2.txt")),true);
pw.println(sc.nextLine()); // AutoFlush Option이 true기 떄문에 자동으로 파일에 쓰여짐
pw.print(sc.nextDouble()); // 문자열 뿐만 아니라 , double도 입력 가능 , BufferedWriter는 문자만 가능
주석에서도 표시 되지만
Bufferwriter는 Chracter , Chracter Array Type만 다룹니다.
PrintWriter는 어느 Type이든 메서드의 parameter로 받을 수 있습니다.
따라서 Chracter, Chracter Array 만 사용한다면 BufferReader 를 그렇지 않다면 PrintWriter를 사용하면 됩니다.
Example 파일 읽고 복사하여 쓰기
List<String> textName = new ArrayList<>();
textName.add("src/main/resources/file1.txt");
textName.add("src/main/resources/file2.txt");
textName.add("src/main/resources/file3.txt");
for (int i = 0; i < textName.size(); i++) {
try (BufferedReader br = new BufferedReader(new FileReader(textName.get(i)))) {
BufferedWriter bw = new BufferedWriter(new FileWriter(textName.get(i) + "_Copy_By_BufferWriter"));
PrintWriter pw = new PrintWriter(new FileWriter(textName.get(i) + "_Copy_By_PrintWriter"),true);
int count = 0;
String str = null;
while ((str = br.readLine()) != null) {
bw.write(str);
bw.newLine();
pw.println(str);
count++;
}
bw.flush();
logger.info("{} 파일은 {}줄로 이루어져 있습니다.", textName.get(i), count);
} catch (FileNotFoundException e) {
logger.warn("파일을 찾지 못했습니다");
} catch (IOException e) {
logger.warn("I/O Stream에 문제가 있습니다");
}
}
다음은 BufferedReader를 이용해 .txt 파일을 읽어 해당 파일이 몇 줄로 이루어져 있는지 확인한 후
Copy_By 라는 이름으로 파일을 복사하는 코드입니다.
BufferedReader를 이용했기 때문에 .txt 파일을 한 줄 단위로 읽게 되고 , 더 이상 읽을것이 없는 경우 null 을 반환하게 되어
파일 읽기가 종료됩니다.
BufferedWriter는 계속해서 Buffer에 한 줄 씩 입력을 받은 후
파일 읽기가 끝난 후에 flush를 사용함으로써 한번에 Buffer안에 data를 내보냈고
PrintWriter의 경우 AutoFlush 옵션을 true로 설정해 별도의 flush 없이 파일에 data가 써집니다.
이해를 돕기 위해 try문 안에서 선언하여 사용했지만 try-with-resources 문을 사용해
코드를 간추리는 방식도 좋아보입니다.
'프로그래밍 기초 > 전산학 기초' 카테고리의 다른 글
[추상클래스 Vs 인터페이스] (0) | 2023.10.22 |
---|---|
클래스 설계와 Abstraction Barrier (0) | 2023.10.22 |
Abstraction(추상화) 기본 개념 - 1편 (0) | 2023.10.20 |
[Static Vs Dynamic] Series (3) | 2023.10.19 |
Thread Safety 개념과 방법 [Monitor,Mutex,Semaphore] (2) | 2023.10.15 |