[Java] Streams and File I/O

immanuelk1m·2023년 5월 23일
1

Java

목록 보기
6/9
post-thumbnail

Stream?
Stream은 input 또는 output data의 흐름을 의미한다.
Java에서 stream은 stream class에 의해 implement 된다.

Text File I/O

File Type

File에는 Text File, Binary File 2가지가 있고,
Text File은 개발자가 볼 수 있는 파일로, text editor로 수정이 가능하고,
Binary File은 그렇지 않다.

Create Text File

PrintWriter

  • PrintWriter 내에는 text file을 작성하기 위한 method들이 정의되어있다.
  • PrintWriter는 java.io package를 호출해야 한다.
  • PrintWriter 생성자에는 path를 작성해 준다.
  • 파일이 존재하는 경우, 기존 파일의 내용은 삭제되고 새로운 데이터로 대체
  • 파일이 없는 경우 있으므로 try - catch 예외처리를 작성해야 한다.
String fileName = "out.txt";
PrintWriter outputStream = null;
try
{
	outputStream = new PrintWriter(fileName);
}
catch(FileNotFoundException e)
{
	System.out.println("Error opening the file " + fileName);
    System.exit(0);
}

Text File 작성 과정

  1. Text File Path Open
  2. pw.println("text") 같은 입력 method로 파일 내용을 입력
  3. 입력한 내용이 메모리상의 buffer가 가득 차게 되면 buffer 내용이 파일 이동
  4. 파일을 닫으면 버퍼가 비워지고, 파일과의 연결이 끊기
  5. 메모리 버퍼에 남아있던 데이터는 파일로 옮겨지고(flush), 파일은 안전하게 닫힙니다.

참고로 outputStream.close() 내에 flush()가 따로 선언되어 있어 flush를 따로 해줄 필요는 없다.


import java.io.PrintWriter;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class TextFileOutputDemo
{
	public static void main (String [] args)
	{
      String fileName = "out.txt";
      PrintWriter outputStream = null;
      try 
      {
        outputStream = new PrintWriter (fileName);
      } 
      catch (FileNotFoundException e) 
      {
        System.out.println ("Error opening the file " + fileName);
        System.exit (0);
      }
      
      System.out.println ("Enter three lines of text:");
      Scanner keyboard = new Scanner (System.in);
      // 입력 class 생성
      
      for (int count = 1 ; count <= 3 ; count++) 
      {
        String line = keyboard.nextLine (); // 내용 입력
        outputStream.println (count + " " + line); // text 파일에 출력
      }
      outputStream.close();
	}
}

Appending Text File

위에는 아무것도 없는 Text File에 파일 내용을 작성했다면 이번에는 기존 Text File에 이어서 작성(Append)하는 방법을 알아보도록 하자.

아래 코드는 file에 마지막 부분 부터 이어서 작성하는 방법이다.

outputStream = new PrintWriter(new FileOutputstream(fileName, true));

Reading Text File

파일을 만들었으니 이번에는 파일을 읽는 방법을 알아보도록 하자.
파일 내용을 읽을 때에는 Scanner Object를 사용한다.
Boolean statement를 사용해 파일의 끝에 도달할 때까지 파일 내용을 불러온다.


import java.util.Scanner;
import java.io.File;
import java.io.FileNotFoundException;
public class TextFileInputDemo
{
public static void main (String [] args)
{
  String fileName = "out.txt";
  Scanner inputStream = null; 
  System.out.println ("The file " + fileName + "\ncontains the following lines:\n");
  try 
  {
  	inputStream = new Scanner (new File (fileName));
    // Scanner Object 사용
  } 
  catch (FileNotFoundException e) 
  {
    System.out.println ("Error opening the file " + fileName);
    System.exit (0);
  }
  while (inputStream.hasNextLine ()) // 끝까지 반복
  {
    String line = inputStream.nextLine ();
    System.out.println (line);
  }
  inputStream.close ();
  }
}

Scanner

Scanner Method

"토큰"이란 문자열에서 구분되는 요소를 말한다.
공백, 탭, 줄바꿈 등의 구분자를 기준으로 토큰이 나뉜다.

hasNext() : 객체에서 다음 토큰이 있는지를 boolean으로 return
hasLine() : 객체에서 줄바꿈한 다음 토큰이 있는지 boolean으로 return
hasNextDouble() : 객체에서 다음 토큰이 Double Type인지를 boolean으로 return
hasNextInt() : 객체에서 다음 토큰이 Int Type인지를 boolean으로 return

Techniques for Any File

File Class

File Object는 단순히 String Object가 아니라 File Name과 여러 State와 Method를 가지고 있는 하나의 객체이다.

new File("file.txt");

File Path

파일이 Java 코드와 같은 폴더에 위치해 있다면 파일명으로 호출이 가능하지만
그렇지 않다면 각 OS에 맞게 절대 경로 혹은 상대경로로 파일을 불러야한다.

UNIX style : /usrbin/ls
Window style : C:\Windows\system32

Constructer

File Class의 생성자는 크게 두 가지가 있다.
부모 FILE class를 상속 받거나, 부모 FILE Path에 하위 경로를 추가하는 방법이 있다.

File parentDir = new File("/Users/user/Documents");
String childPath = "file.txt";
File file = new File(parentDir, childPath);

String parentPath = "/Users/user/Documents";
String childPath = "file.txt";
File file = new File(parentPath, childPath);

Methods

  • canRead(): 해당 파일을 읽을 수 있는지 여부를 확인
    읽기 가능하다면 true를 반환하고, 그렇지 않으면 false를 반환

  • canWrite(): 해당 파일에 쓸 수 있는지 여부를 확인
    쓰기 가능하면 true를 반환하고, 그렇지 않으면 false를 반환합니다.

  • delete(): 해당 파일을 삭제
    파일이 성공적으로 삭제되면 true를 반환하고, 그렇지 않으면 false를 반환

  • exists(): 해당 파일이 존재하는지 여부를 확인
    파일이 존재하면 true를 반환하고, 그렇지 않으면 false를 반환

  • getName(): 파일의 이름을 반환
    경로를 포함하지 않고 파일 이름만 반환한다.

  • getPath(): 파일의 경로를 반환
    절대 경로나 상대 경로 모두 가능하며, 파일의 전체 경로를 반환한다.

  • length(): 파일의 크기를 바이트 단위로 반환
    파일의 크기를 나타내는 정수 값을 반환한다.

Open CSV File

csv파일은 각 요소가 comma(',')로 나뉘어져있는 파일이다.

String line = "1,2,3,4,5";
String[] arr = line.split(",");

위 명령어로 CSV파일 라인 각 요소들을 처리할 수 있다.

Binary File I/O

Binary File

Binary File이란 이진 형태로 데이터를 저장한 파일이다.
따라서 개발자가 이해하려고 해도 알아보기 어려운 형태로 있어 이해하기 어렵다.

Binary File를 쓰는 이유

'1234567890'를 Text File에 작성한다고 하면
char(1 byte)를 10번 작성 후, additional 3 byte까지 합하여 13 byte를 사용하게 된다.

하지만 Binary File에 작성한다고 하면
int(4 byte)를 한 번만 작성한 후 additional 6 byte까지 합하여 10 byte만 사용해 효율적이다.

만약 20개의 10자리 int 값과 20개의 10자리 string 값을 추가 한다고 하면 byte 수의 차이는 더 커지게 된다.

20 int = 4 20 + 6 = 86 byte
20 string = (1
10) * 20 + 3 = 203 byte

Create a Binary File

ObjectOutputStream Class

ObjectOutputStream Class는 primitive types, strings, 여러 objects 값들을
Binary File에 저장하게 한다.

ObjectOutputStream outputStream =
new ObjectOutputStream (new FileOutputStream (fileName));

ObjectOutputStream Class Methods

writeInt(int n): 정수 값을 이진 파일에 쓰는 Method
주어진 정수 값을 이진 형식으로 변환하여 파일에 저장

writeChar(int c): 문자 값을 이진 파일에 쓰는 Method
주어진 정수 값을 문자로 변환하여 파일에 저장

writeBoolean(boolean b): 불리언 값을 이진 파일에 쓰는 Method
불리언 값을 1바이트로 변환하여 파일에 저장, true는 1로, false는 0으로 저장

writeUTF(String str): UTF-8 형식으로 인코딩된 문자열을 이진 파일에 쓰는 Method
문자열을 UTF-8로 인코딩한 후, 길이 정보와 함께 파일에 저장
writeUTF()는 writeInt와 달리 문자열의 길이에 따라 저장되는 byte 수가 다르므로
가변적이다.

참고로 위의 method에는 IOException이 모두 적용 되어있다.
IOException는 파일이 존재하지 않는 경우, 파일에 쓰기 권한이 없는 경우, 네트워크 연결이 끊긴 경우 등 입력이 원할하지 않을 때 Exception을 throw 한다.

만약 인코딩방식을 ASCII로 작성하고 싶다면 PrintWriter class를 사용하면 된다.

ObjectOutputStream Filters

파일을 작성할 때, 여러 옵션을 추가해 파일을 효율적으로 작성할 수도 있다.

ObjectOutputStream outputStream = new ObjectOutputStream
(
  new BufferedOutputStream
  	(new FileOutputStream(fileName))
);

FileOutputStream: 실제로 파일에 데이터를 쓰기 위한 fileName이라는 파일에 대한 출력 스트림을 생성

BufferedOutputStream: 데이터를 버퍼에 저장하고 필요에 따라 파일로 효율적으로 전송

ObjectOutputStream: 기본 타입, 문자열 및 객체를 이진 형식으로 변환하여 파일에 저장

위의 코드는 fileName이라는 파일에 객체를 저장하기 위한 ObjectOutputStream을 생성하는 동안 BufferedOutputStream과 FileOutputStream를 사용한다.

Writing Primitive Values to a Binary File

Text File과 달리, Binary File은 println()으로 작성할 수 없고
대신 writeInt()와 같은 method를 사용해서 작성해야 한다.

Reading from a Binary File

Reading Method를 사용하여 Binary File에서 파일을 읽어 올 때, 각 type에 맞게 읽어야 한다. 따라서 writeInt로 쓴 정수는 readInt로 읽어야 한다.

ObjectInputStream Class

ObjectInputStream in = new ObjectInputStream(
new BufferedInputStream(
new FileInputStream(fileName)));

위의 예시에서 Output -> Input으로 바뀐 것 말고 큰 틀은 같다.

ObjectInputStream Class Methods

readInt(), readChar(), readBoolean() : 기능은 write의 반대이고, EOFException이 추가 되었다.
readUFT(), readObject() 또한 같다.

EOFException은 파일의 끝에 도달하게 되면 예외 처리를 한다.

try 
{
  while (true) 
  {
    int anInteger = inputStream.readInt ();
    System.out.println (anInteger);
  }
} 
catch (EOFException e) 
{
  System.out.println ("Reached end of the file.");
}

File I/O With Objects and Arrays

Object serialization

Text나 String 말고 객체를 저장하려면 Java에서 제공하는 Object serialization를 사용하면 된다.

Serialization(직렬화)란 Java 시스템 내부에서 사용되는 객체 또는 데이터를 외부의 Java 시스템에서도 사용할 수 있도록 바이트(byte) 형태로 데이터 변환하는 기술이다.

class Species implements Serializable
{
...
}
outputStream.writeObject (califCondor);
readOne = (Species) inputStream.readObject ();

기존 class에 Serializable interface를 implement 해주게 되면, Object를 읽고 쓸 수 있게 된다.

아래 블로그에 잘 정리 되어있다.
https://shorturl.at/oxzHK

  • 예외로 변수 type 앞에 transient를 작성하면 해당 변수는 Serialization과정에서 제외 처리되고 Null 값을 대입하게 된다.

  • Array 또한 Object로 read/writeObject를 사용해 전체 array를 binary file로 작성이 가능하다.

  • 만약 class가 Serialization이 되어있지 않다면, read/writeObject가 불가능하다.

profile
개발 새발

0개의 댓글