java, spring

[Java] 파일 입출력 java.io.File / InputStream, OutputStream

isaac.kim 2023. 5. 29. 14:21
728x90
반응형

[Java] 파일 입출력 java.io.File / InputStream, OutputStream

 

Java에서 파일을 어떻게 처리하는지 복습하는 차원에서 이 글을 작성하게 되었습니다. 글을 잘 쓰진 않지만 도움이 되는 글을 쓰고 싶어 글을 작성하게 되었습니다. 글을 읽고 도움이 되면 좋겠습니다. 그럼 Java에서 File 처리하는 방법에 대해 알아보겠습니다.

 

I/O Input, Output

프로그래밍 파일 처리에서 대표적인 키워드가 아마도 I/O일 것입니다. I/O는 input과 output의 앞 글자를 따와서 붙여놓은 키워드입니다. 파일 처리에서 input과 output이 자주 사용되기 때문에 File에서 IO라는 키워드가 항상 같이 붙어 있습니다. 컴퓨터 바탕 화면에 메모장을 만들어서 기록하는 것도 File I/O가 발생하고 있는 것입니다.

I / O : Input / Output의 약자
예) 메모장을 열고 기록하는 것 input, 저장해서 바탕 화면에 메모장 파일을 생성하는 것 output.

앞으로 사용할 Java의 File 클래스 또한 java.io 패키지에 포함되어 있습니다.

 

컴퓨터 기준에서 I/O는 어떻게 될까요?

 

컴퓨터에서 외부 장치로 처리하는 것을 output

외부장치에서 컴퓨터로 처리하는 것을 input

으로 볼 수 있습니다.

 

컴퓨터에서 외부 장치인 하드디스크나 CD, USB 등에 저장하는 것은 Output 처리

컴퓨터로 파일 열기, CD 파일 열기, USB 파일 열기 등이 Input 처리라고 볼 수 있습니다.

 

Output은 Write(쓰기), Create(생성하기) 처리를 주로 하고 Input은 Read(읽기)를 주로 처리하고 있습니다.

File I/O는 FIle의 Input / Output 즉, 파일의 입출력을 말합니다.

 

Stream

위에서 I/O Input, Output에 대해서 알아보았습니다. 다음으로 Input / Output을 실제로 수행하는 Stream에 대해 알아보겠습니다. 파일을 읽고, 쓰고 저장하는데 실제로 데이터를 읽고 쓰는 데 사용되는 장치(인터페이스)를 Stream이라고 합니다. 데이터가 이동하는 쪽으로 전달하는 것이 Stream입니다.

 

물이 한쪽 방향으로 흐르는 것 같이 데이터도 한쪽으로만 처리할 수 있어서, 스트림도 입력 스트림출력 스트림으로 구분되어 있습니다.

 

 

 

File Class

Java에서 사용되는 File 클래스는 java.io 패키지에 있습니다.

import java.io.File;

File 클래스를 사용해 File 및 Directory path를 입력해 해당 위치의 파일을 처리할 수 있습니다. 설정한 경로에 파일이 존재해도 되고, 존재하지 않아도 File 클래스를 사용할 수 있습니다.

 

먼저 File 클래스 생성자를 보면 다음과 같이 사용을 할 수 있습니다.

File(String pathname) // pathname에는 파일 경로명
File(String parent, String child) // parent 폴더 경로, child 파일명
File(URI uri) // URI 파일

 

File 클래스를 사용해서 파일을 생성해 보겠습니다.

절대 경로, 상대 경로 모두 사용이 가능하고, 운영체제 관계없이 경로는 슬래쉬(/), 백슬래쉬(\) 모두 사용할 수 있습니다.

import java.io.File;
import java.io.IOException;

public class Application {
    public static void main(String[] args) throws IOException {
        File file1 = new File("./test/file1.txt");
        File file2 = new File("./test","file2.txt");
        
        file1.createNewFile();
        file2.createNewFile();
    }
}

텅 빈 text 파일 2개가 생성되었습니다.

방금 앞에서 File 클래스의 주요 메서드 중 createNewFile() 메서드를 사용해서 파일을 생성해 보았습니다. createNewFile() 말고 다른 주요 메서드는 어떤 것이 있는지 보겠습니다.

/**
* 파일이 없으면 생성 후 true, 있으면 false return
*/
createNewFile() 


/**
* prefix : 앞에 붙는 이름
* suffix : 확장자
* dir : 파일 경로
*/
createTempFile(String prefix, String suffix, File dir) 

/**
* 프로그램 종료 시 삭제
*/
deleteOnExit()

/**
* 파일 삭제
*/
delete()

/**
* 파일 크기 반환
*/
long length()

다른 주요 메서드로도 금방 확인할 수 있어서 따로 예제를 작성하진 않겠습니다. 생성했던 File 객체에 delete(), deleteOnExit() 메서드를 실행하면 파일이 삭제되는 것을 확인할 수 있습니다.

 

BYTE Input / Output

위에서 빈 파일을 생성하는 방법을 알아보았는데, 실제로 파일이 의미가 있는 것은 데이터가 담겨 있기 때문입니다. 앞에서 파일의 데이터를 읽고, 쓰는데 필요한 것이 Stream이라는 장치라고 했습니다. 이 Stream은 데이터를 바이트 단위로 데이터를 전송합니다.

 

바이트(Byte) 입력(Input)과 출력(Output)은 다음 두 클래스를 사용합니다.

Class
	FileOutputStream
	FileInputStream

FileOutputStream은 write() 메서드를 주로 사용하여서 바이트 데이터를 출력하고,

FileInputStream은 read() 메서드를 주로 사용하여서 바이트 데이터를 입력받습니다.

(입력 데이터의 끝은 -1입니다.)

 

 

Byte Output

 

먼저 FileOutputStream과 write() 메서드를 사용해서 파일에 내용을 써보겠습니다.

String dir = "./src/resources/file"; // 파일을 저장할 위치 지정
File file = new File(dir, "file.txt");
FileOutputStream fos = new FileOutputStream(file);
fos.write(65);

FileOutputStream에서 write() 메서드를 사용하면 write() 메서드의 매개 변수로 들어온 내용을 파일에 쓰게 됩니다. 동시에 파일이 생성됩니다. 입력한 65는 파일 내에서 아스키 값 65인 문자  'A'가 출력됩니다.

FileOutputStream fos = new FileOutputStream(file, true);

생성자를 호출할 때 두 번째 매개 변수로 boolean true를 주고 FileOutputStream 생성해 쓸 때, 파일이 이미 존재하면 파일 내용에 이어 쓰게 됩니다. FileOutputStream 생성자 호출 부분의 매개 변수를 수정하고 실행한 뒤 파일을 열어보면 'AA'가 작성된 것을 확인할 수 있습니다.

 

문자열은 어떻게 작성하는지 다음 코드를 보며 확인해 보겠습니다.

byte[] by = "Hello World".getBytes();   
fos.write(by);

FileOutputStream이 바이트 단위로 데이터를 전송하기 때문에 전송하고 싶은 문자열을 바이트(Byte)로 파일 쓰기(write)를 진행합니다. byte[] by = "Hello World".getBytes() 구문은, "Hello World"라는 문자열의 바이트 데이터를 구해서 바이트 배열 타입 변수 byte[] by에 바이트 데이터를 할당합니다. 그리고 FileOutputStream 객체(fos)로 바이트 데이터를 write() 메서드에 넣어서 파일 쓰기를 진행합니다. 생성되어 있는 텍스트 파일을 열어 보면 "Hello World" 텍스트가 추가된 것을 확인할 수 있습니다.

write() 메서드를 조금 더 알아보겠습니다.

이번엔 전달하려는 문자열을 잘라서 넣고 싶을 때 예시 입니다.

문자열 "Hello World"에서 여섯 번째 글자부터 네 글자를 사용하고 싶다면 다음과 같이 작성할 수 있습니다.

byte[] by = "Hello World".getBytes();   
fos.write(by, 6, 4); // 매개 변수 : 바이트 문자열, 시작 문자, 글자 수
// Worl

 

Byte Input

 

앞에서 '파일 쓰기'에 대해 알아보았습니다. 다음은 '파일 읽기'에 대해서 알아보겠습니다.

파일 읽기는 FileInputStream 클래스를 사용합니다.

 

FileInputStream 생성자를 호출하면서 읽을 파일(File) 객체를 매개 변수로 넣어줍니다. new FileInputStream(file)

String dir = "./src/resources/file";
File file = new File(dir, "file.txt");
FileInputStream fis = new FileInputStream(file);

그리고 FileInputStream 객체(fis)로 read() 메서드를 호출하여 바이트 단위로 읽습니다.

int res = fis.read(); // 한 바이트 읽기

그런데 위 코드는 한 바이트만 읽어 반환하고 끝납니다. 계속 읽어 나가려면 반복해서 read()를 호출을 해야 하고, 우리가 눈으로 보려면 읽은 데이터를 출력해야 합니다. 반복자를 추가하고, 파일의 끝을 파악할 수 있는 코드를 추가하여 반복자를 멈추어 프로그램을 정상적으로 종료시키는 코드를 추가합니다. 파일의 끝이 아니면 읽은 데이터를 계속 출력합니다.

while(true) {
    int res = fis.read(); // 한 글자 씩 아스키 코드 값으로 받는다.
    if(res == -1) break; // Byte 입력에서 파일의 끝(EOF) 값은 -1 입니다.
    System.out.print((char) res);
}

바이트로 읽은 데이터는 int type으로 반환하게 되는데 이 데이터는 아스키(ASCII) 코드 번호인 정수형 데이터로 표현되어서 이를 다시 character type으로 변환하여 출력함으로써 파일 데이터의 문자들을 읽고 출력할 수 있습니다. 모든 문자가 출력되면 문자열을 읽어서 출력한 것처럼 보이게 됩니다.

위 '파일 읽기'에 대한 설명의 전체 코드입니다.

String dir = "./src/resources/file";
File file = new File(dir, "file.txt");

FileInputStream fis = new FileInputStream(file);

while(true) {
    int res = fis.read(); // 한 글자 씩 아스키 코드 값으로 받는다.
    if(res == -1) break; // Byte 입력에서 파일의 끝(EOF) 값은 -1 입니다.
    System.out.print((char) res);
}

 

바이트 입출력에서 Java Data Type 입출력

위에서 파일 입출력을 할 때 FileInputStream, FileOutputStream을 통해서 바이트(Byte) 기준으로 입출력을 진행했습니다. 이번에는 바이트를 입출력하는 것이 아닌 Java에서 사용되는 여러 자료형들로 입출력하는 방법에 대해 알아보겠습니다.

 

Java에서 사용되는 다른 자료형들을 파일에 입출력하려면 File I/O Stream 외 다른 2개의 Stream이 더 필요합니다. 

Buffer I/O Stream과 Data I/O Stream입니다. 지금 설명하려는 방법에서는 3개의 Stream 클래스가 사용됩니다.

 

1. 바이트 데이터 출력

- FileOutputStream

- BufferedOutputStream

- DataObjectStream

 

2. 바이트 데이터 입력

- FileInputStream

- BufferedInputStream

- DataInputStream

 

 

바이트 데이터 출력

DataOutputStream 먼저 보겠습니다.

 

File을 할당받은 FileOutputStream 객체를 BufferedOutputStream에 넣고, 다시 BufferedOutputStream을 DataOutputStream 클래스에 넣어서 DataOutputStream 내의 메서드로 잘라 파일에 저장합니다.

File > FileOutputStream > BufferedOutputStream > DataOutputStream

new DataOutputStream(new BufferedOutputStream(new FileOutputStream(new File())))

다음 예시 코드를 보면 위 설명과 같습니다. 그런데 아래 코드를 실행해서 파일을 확인해 보면 데이터가 이상하게 들어가 있는 것을 볼 수 있습니다. 위 소제목에서처럼 바이트 입출력을 하기 때문에 그렇습니다.

String dir = "./src/resources/file";
File file = new File(dir, "file1.txt");

FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos);
DataOutputStream dos = new DataOutputStream(bos);

int var1 = 65;
double var2 = 3.14;
String var3 = "https://lifere.tistory.com/";

dos.writeInt(var1);
dos.writeDouble(var2);
dos.writeUTF(var3);
dos.close();

실제로 개발 환경에서 파일을 확인해 보면 다음과 같이 저장이 되어 있는 것을 확인할 수 있습니다.

위에서 저장한 값이 65, 3.14 그리고 문자열이었는데 다르게 저장되는 것 같다고 보실 수 있는데,

바이트 단위로 데이터가 저장되기 때문에 그렇습니다.

 

바이트 데이터 입력

DataInputStream을 사용해 보겠습니다.

다시 파일을 똑같이 int, double, String으로 읽어 들여서 Console에 출력을 해보겠습니다.

String dir = "./src/resources/file";
File file = new File(dir, "file1.txt");

FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
DataInputStream dis = new DataInputStream(bis);

int v1 = dis.readInt();
double v2 = dis.readDouble();
String v3 = dis.readUTF();

System.out.println("v1 : " + v1);
System.out.println("v1 : " + v2);
System.out.println("v1 : " + v3);

dis.close();

다음은 출력 결과입니다. 텍스트 파일에 저장되어 있는 보이는 데이터와 다르게 정상적으로 입력했던 값 그대로 출력되는 것을 확인할 수 있습니다. DataInputStream을 활용해서 Java primitive data type 그대로 갖고 와서 사용할 수 있습니다.

 

 

 

내용이 조금 긴 것 같지만, 그래도 계속 이어서 써보도록 하겠습니다.

 

열심히 콘텐츠를 만드는 제게 힘이 될 수 있도록 아래 광고 한 번 보고 와주시면 감사하겠습니다. : )

반응형

 

 

파일 텍스트 입력

다음 클래스들을 사용해 텍스트를 파일에 입력하는 방법에 대해 알아보겠습니다.

FileReader, BufferedReader 이 두 클래스를 사용해 파일을 읽고, 파일 내용의 텍스트를 입력받을 수 있습니다.

 

텍스트 파일을 준비합니다. 파일에 다음과 같은 내용을 작성하였습니다.

파일명 : file.txt

 

Hello world
My name is Isaac Kim

파일 내용을 읽어 텍스트를 출력하는 코드를 다음과 같이 작성합니다.

String dir = "./src/resources/file";
File file = new File(dir, "file.txt");

FileReader fr = new FileReader(file);
BufferedReader br = new BufferedReader(fr);
while(true) {
    String msg = br.readLine();
    if (msg == null) break; // text 파일의 끝은 null
    System.out.println(msg);
}

파일 내용, 텍스트를 입력받아서 출력하는 결과를 확인했습니다.

 

 

Object 입출력

1. 직렬화

객체에 대한 입출력을 사용하기 전에 '직렬화'에 대해 알아보겠습니다.

직렬화는 객체를 컴퓨터에 입출력을 하는 과정에서 서로 다른 메커니즘을 일치시켜주는 작업을 말합니다.

 

직렬화(Serialization), 역직렬화는 객체 데이터를 스트림으로 만들어 주거나, 스트림을 객체로 만들어 주는 것입니다.

직렬화는 인터페이스로 볼 수 있습니다.

 

사용방법은 직렬화를 하고자 하는 클래스에 Serializable 인터페이스를 상속받으면 됩니다.

 

2. Object 출력에 사용되는 메서드

- FileOutputStream

- BufferedOutputStream

- ObjectOutputStream

 

3. Object 입력에 사용되는 메서드

- FileInputStream

- BufferedInputStream

- ObjectInputStream

 

 

Object 출력하기

 

출력할 객체 클래스입니다. 객체를 파일에 쓰기 위해 직렬화 작업을 해줍니다. ObjectVo 클래스에 Serializable 인터페이스를 상속합니다.

public class ObjectVo implements Serializable {
    String name = "isaac";
    int year = 2023;
}

위에서 소개한 클래스를 사용해 객체를 파일로 출력합니다.

- FileOutputStream

- BufferedOutputStream

- ObjectOutputStream

 

String dir = "./src/resources/file";
File file = new File(dir, "file3.txt");

FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos);
ObjectOutputStream oos = new ObjectOutputStream(bos);
ObjectVo obj = new ObjectVo();
oos.writeObject(obj);
oos.close();

실행하면 바이트 데이터를 입출력했던 것처럼 파일에 쓰입니다.

 

다시 읽어 들였을 때, 위 ObjectVo의 내용이 출력된다면 객체 입출력이 제대로 된 것으로 볼 수 있습니다.

 

 

 

※ transient 키워드를 사용하면 File, Network 등에서 전송하지 못하도록 지정할 수 있습니다.

- ex) transient String name

 

 

 

Object 입력하기

위에서 소개한 입력 클래스를 사용해서 파일에서 객체를 읽습니다.

- FileInputStream

- BufferedInputStream

- ObjectInputStream

String dir = "./src/resources/file";
File file = new File(dir, "file3.txt");

FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
ObjectInputStream ois = new ObjectInputStream(bis);

ObjectVo objectVo = (ObjectVo) ois.readObject();
System.out.println("object name : "+objectVo.name);
System.out.println("object year : "+ objectVo.year);

ois.close();

출력 결과를 보면 파일에 썼던 객체 내용들을 읽어와서 객체 내용 그대로 출력한 것을 볼 수 있습니다.

 


Java에서 파일을 다루는 방법에 대해서 알아보았습니다. 글이 좀 길어진 것 같지만 Java로 파일을 처리할 때 어떻게 처리할 수 있는지, 공부에 도움이 되었으면 좋겠습니다. 긴 글 읽어주셔서 감사합니다. : )

 

예제 코드 repo

728x90
반응형