파일은 기본적이면서도 가장 많이 사용되는 입출력 대상이기 때문에 중요하다. 그래서 관련된 기본적인 내용뿐만 아니라 다양한 활용 예제들을 볼것이다. 그래서 이번 글은 굉장히 길 것이다. 하지만, 이들을 응용해서 다양한 예제들을 만들어 보면 굉장히 도움이 될 것이다.
자바에서는 File 클래스를 통해서 파일과 디렉토리를 다룰 수 있도록 하고 있다. 그래서 File인스턴스는 파일일 수도 있고, 디렉토리일 수도 있다. 앞으로 File클래스의 생성자와 메서드를 관련된 것들끼리 나누어서 예제와 함께 알아보자.
먼저 File의 생성자와 경로에 관련된 메서드를 알아보자.
생성자 / 메서드 | 설명 |
File(String fileName) | 주어진 문자열(fileName)을 이름으로 갖는 파일을 위한 File인스턴스를 생성한다. 파일뿐만 아니라 디렉토리도 같은 방법으로 다룬다. 여기서 fileName은 주로 경로(path)를 포함해서 지정해주지만, 파일 이름만 사용해도 되는데 이 경우 프로그램이 실행되는 위치가 경로(path)로 간주된다. |
File(String pathName, String fileName) File(File pathName, String fileName) |
파일의 경로와 이름을 따로 분리해서 지정할 수 있도록 한 생성자. 이 중 두 번째 것은 경로를 문자열이 아닌 File인스턴스인 경우를 위해서 제공된 것이다. |
File(URI uri) | 지정된 uri로 파일을 생성 |
String getName() | 파일이름을 String으로 반환 |
String getPath() | 파일의 경로(path)를 String으로 반환 |
String getAbsolutePath() File getAbsoluteFile() |
파일의 절대경로를 String으로 반환 파일의 절대경로를 File로 반환 |
String getParent() File getParentFile() |
파일의 조상 디렉토리를 String으로 반환 파일의 주상 디렉토리를 File로 반환 |
String getCanonicalPath() File getCanonicalFile() |
파일의 정규경로를 String으로 반환 파일의 정규경로를 File로 반환 |
멤버변수 | 설명 |
static String pathSeparator | OS에서 사용하는 경로(path)구분자. 윈도우 ";", 유닉스 ":" |
static char pathSeparatorChar | OS에서 사용하는 경로(path)구분자. 윈도우 ";", 유닉스 ":" |
static String separator | OS에서 사용하는 이름 구분자. 윈도우 "₩", 유닉스 "/" |
static char separatorChar | OS에서 사용하는 이름 구분자. 윈도우 "₩", 유닉스 "/" |
위 표에서 알 수 있는 것과 같이 파일의 경로(path)와 디렉토리나 파일의 이름을 구분하는 데사용되는 구분자가 OS마다 다를 수 있기 때문에, OS독립적으로 프로그램을 작성하기 위해서는 반드시 위의 멤버변수들을 이용해야 한다. 만일 윈도우에서 사용하는 구분자를 코드에 직접 적어 놓았다면, 이 코드는 다른 OS에서는 오류를 일으킬 수 있다.
import java.io.*;
class FileEx1 {
public static void main(String args[]) {
File f = new File("c:\\jdk17.01\\work\\ch15\\FileEx1.java");
String fileName = f.getName();
int pos = fileName.lastIndexOf(".");
System.out.println("경로를 제외한 파일이름 : " + f.getName());
System.out.println("확장자를 제외한 파일이름 : " + fileName.substring(0,pos));
System.out.println("확장자 : " + fileName.substring(pos+1));
System.out.println("경로를 포함한 파일이름 : " + f.getPath());
System.out.println("파일의 절대경로 : " + f.getAbsolutePath());
System.out.println("파일의 정규경로 : " + f.getCanonicalPath());
System.out.println("파일이 속해 있는 디렉토리 : " + f.getParent());
System.out.println("");
System.out.println("File.pathSeparator : " + File.pathSeparator);
System.out.println("File.pathSeparatorChar : " + File.pathSeparatorChar);
System.out.println("File.separator : " + File.separator);
System.out.println("File.separatorChar : " + File.separatorChar);
System.out.println("");
System.out.println("user.dir : " + System.getProperty("user.dir"));
System.out.println("sun.boot.class.path : " + System.getProperty("sun.boot.class.path"));
}
}
File인스턴스를 생성하고 메서드를 이용해서 파일의 경로와 구분자 등의 정보를 출력하는 예제이다. 결과를 보면 어떤 결과를 얻기 위해서는 어떤 메서드를 사용해야하는지 감이 잡힐 것이다.
절대경로(absolute path)는 파일시스템의 루트(root)로부터 시작하는 파일의 전체 경로를 의미한다. OS에 따라 다르지만, 하나의 파일에 대해 둘 이상의 절대경로가 존재할 수 있다. 현재 디렉토리를 의미하는 '.'와 같은 기호나 링크를 포함하고 있는 경우가 이에 해당한다. 그러나 정규경로(canonical path)는 기호나 링크 등을 포함하지 않는 유일한 경로를 의미한다.
예를 들어, 'C:\jdk17.01\work\ch15\FileEx1.java'의 또 다른 절대경로는 'C:\jdk17.01\work\ch15\.\FileEx1.java' 가 있지만, 정규경로는 'C:\jdk17.01\work\ch15\FileEx1.java' 단 하나 뿐이다.
시스템속성 중에서 user.dir의 값을 확인하면 현재 프로그램이 실행 중인 디렉토리를 알 수 있다. 그리고 우리가 OS의 시스템변수로 설정하는 classpath외에 sun.boot.class.path라는 시스템속성에 기본적인 classpath가 있어서 기본적인 경로들은 이미 설정되어 있다. 그래서 처음에 JDK 설치 후 classpath를 따로 지정해주지 않아도 되는 것이다. 이 속성은 JDK1.2 이후에 추가된 것이라 그 이전의 버전에서는 rt.jar와 같은 파일을 classpaht에 추가해주어야 했다.
예제에서 사용된 'File f = new File("c:\\jdk17.01\\work\\ch15\\FileEx1.java");' 대신 다른 생성자를 사용해서 File인스턴스를 생성할 수 있다.
File f = new File("c:\\jdk17.01\\work\\ch15\\FileEx1.java");
또는
File dir = new File("c:\\jdk17.01\\work\\ch15");
File f = new File(dir, "FileEx1.java");
한 가지 더 알아두어야 할 것은 File인스턴스를 생성했다고 해서 파일이나 디렉토리가 생성되는 것은 아니라는 것이다. 파일명이나 디렉토리명으로 지정된 문자열이 유효하지 않더라도 컴파일 에러나 예외를 발생시키지 않는다.
새로운 파일을 생성하기 위해서는 File인스턴스를 생성한 다음, 출력스트림을 생성하거나 createNewFile()을 호출해야한다.
1. 이미 존재하는 파일을 참조할 때
File f = new File("c:\\jdf17.01\\work\\ch15", "FileEx1.java");
2. 기존에 없는 파일을 새로 생성할 때
File f = new File("c:\\jdf17.01\\work\\ch15", "NewFile.java");
createNewFile();
앞으로 소개할 예제에 사용된 메서드이다. 가볍게 보고 예제를 통해서 그 사용법은 자세히 알아보도록 하자.
메서드 | 설명 |
boolean canRead() | 읽을 수 있는 파일인지 검사한다. |
boolean canWrite() | 쓸 수 있는 파일인지 검사한다. |
boolean canExecute() | 실행할 수 있는 파일인지 검사한다. |
int compareTo(File pathname) | 주어진 파일 또는 디렉토리를 비교한다. 같으면 0을 반환하며, 다르면 1 또는 -1을 반환한다.(Unix 시스템에서는 대소문자를 구별하며, Windows 에서는 구별하지 않는다.) |
blloean exists() | 파일이 존재하는지 검사한다. |
boolean isAbsolute() | 파일 또는 디렉토리가 절대경로명으로 지정되었는지 확인한다. |
boolean isDirectory() | 디렉토리인지 확인한다. |
boolean isFile() | 파일인지 확인한다. |
boolean isHidden() | 파일의 속성이 '숨김(Hidden)'인지 확인한다. 또한 파일이 존재하지 않으면 false를 반환한다. |
boolean createNewFile() | 아무런 내용이 없는 새로운 파일을 생성한다.(단, 생성하려는 파일이 이미 존재하면 생성되지 않는다.) File f = new File("c:\\jdk17.01\\work\\test3.java"); f.createNewFile(); |
static File createTempFile(String prefix, String suffix) | 임시파일을 시스템의 임시 디렉토리에 생성한다" System.out.println(File.createTempFile("work", "tmp"); |
static File createTempFile(String prefix, String suffix, Filedirectory) | 임시파일을 시스템의 지정된 디렉토리에 생성한다. |
boolean delete() | 파일을 삭제한다. |
void deleteOnExit() | 응용 프로그램 종료시 파일을 삭제한다. 주로 실행 시 작업에 사용된 임시파일을 삭제하는데 사용된다. |
boolean equals(Object obj) | 주어진 객체(주로 File인스턴스)가 같은 파일인지 비교한다.(Unix 시스템에서는 대소문자를 구별하며, Windows 에서는 구별하지 않는다.) |
long lastModified() | 파일의 마지막으로 수정된 시간을 지정된 시간을 반환 |
long length() | 파일의 크기를 반환한다. |
String[] list() | 디렉토리의 파일목록(디렉토리 포함)을 String배열로 반환한다. |
String[] list(FilenameFilter filter) File[] list(FilenameFilter filter) |
FilenameFileter인스턴스에 구현된 조건에 맞는 파일을 String배열(File배열)로 반환한다. |
File[] listFiles() File[] listFiles(FileFilter filter) File[] listFiles(FilenameFilter f) |
디렉토리의 파일목록(디렉토리 포함)을 File배열로 반환(filter가 지정된 경우에는 filter의 조건과 일치하는 파일만 반환) |
static File[] listRoots() long getFreeSpace() long getTotalSpace() long getUsableSpace() |
컴퓨터의 파일시스템의 root의 목록(floppy, CD-ROM, HDD drive)을 반환(예: A:\, C:\, D:\) get으로 시작하는 메서드들은 File이 root일 때, 비어있는 공간, 전체 공간, 사용가능한 공간을 바이트 단위로 반환 |
boolean mkdir() boolean mkdirs() |
파일에 지정된 경로로 디렉토리(폴더)를 생성. 성공하면 true mkdirs는 필요하면 부모 디렉토리까지 생성 |
boolean renameTo(File dest) | 지정된 파일(dest)로 이름을 변경 |
boolean setExecutable(boolean executable) boolean setExecutable(boolean executable, boolean ownerOnly) boolean setReadable(boolean readable) boolean setReadable(boolean readable, boolean ownerOnly) boolean setReadOnly() boolean setWritable(boolean writable) boolean setWritable(boolean writable) boolean setWritable(boolean writable, boolean ownerOnly) |
파일의 속성을 변경한다. OwnerOnly가 true이면, 파일의 소유자만 해당 속성을 변경할 수 있다. |
boolean setLastModified(long t) | 파일의 마지막으로 수정된 시간을 지정된 시간(t)로 변경 |
Path toPath() | 파일을 Path로 변환해서 반환 |
URI toURI() | 파일을 URI로 변환해서 반환 |
import java.io.*;
class FileEx2 {
public static void main(String args[]) {
if(args.length != 1) {
System.out.println("USAGE : java FileEx2 DIRECTORY");
System.exit(0);
}
File f = new File(args[0]);
if(!f.exists() || !f.isDirectory()) {
System.out.println("유효하지 않은 디렉토리 입니다.");
System.exit(0);
}
File[] files = f.listFiles();
for(int i=0; i<files.length; i++){
String fileName = files[i].getName();
System.out.println(files[i].isDirectory() ? "[" + fileName + "]" : fileName);
}
}
}
지정한 디렉토리(폴더)에 포함된 파일과 디렉토리의 목록을 보여주는 예제이다.
import java.io.*;
class FileEx3 {
static int totalFiles = 0;
static int totalDirs = 0;
public static void main(String args[]) {
if(args.length != 1) {
System.out.println("USAGE : java FileEx3 DIRECTORY");
System.exit(0);
}
File f = new File(args[0]);
if(!f.exists() || !f.isDirectory()) {
System.out.println("유효하지 않은 디렉토리 입니다.");
System.exit(0);
}
printFileList(dir);
System.out.println();
System.out.println("총 " + totalFiles + " 개의 파일");
System.out.println("총 " + totalDirs + " 개의 디렉토리");
}
public static void printFileList(File dir) {
System.out.println(dir.getAbsoultePath() + " 디렉토리");
File[] files = dir.listFiles();
ArrayList subDir = new ArrayList();
for(int i=0; i<files.length; i++){
String filename = files[i].getName();
if(files[i].isDirectory()) {
filename = "[" + filename + "]";
subDir.add(i + "");
}
System.out.println(filename);
}
int dirNum = subDir.size();
int fileNum = files.length - dirNum;
totalFiles += fileNum;
totalDirs += dirNum;
System.out.println(fileNum + " 개의 파일, " + dirNum + " 개의 디렉토리");
System.out.println();
for(int i=0; j<subDir.size(); i++){
int index = Integer.parseInt((String)subDir.get(i));
printFileList(files[index]);
}
}
}
이전 예제를 발전시켜서 서브디렉토리와 그에 포함된 파일과 디렉토리의 목록까지 보여주도록 하였다.
printFileList(File dir)는 디렉토리에 포함된 파일과 디렉토리의 목록을 출력하는 메서드인데 재귀호출을 이용하였다.
먼저 파일의 목록을 출력하고 디렉토리인 경우 ArrayList에 담았다가 각 디렉토리에 대해 printFileList(File dir)를 재귀호출한다.
사실 ArrayList에 담지 않고 재귀호출만을 이용해도 처리가 가능하나, 보다 정돈된 형태로 출력하기 위해서 이 방식을 선택했다.
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
class FileEx4 {
public static void main(String[] args) {
String currDir = System.getProperty("user.dir");
File dir = new File(currDir);
File[] files = dir.listFiles();
for(int i=0; i<files.length; i++) {
File f = files[i];
String name = f.getName();
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mma");
String attribute = "";
String size = "";
if(files[i].isDirectory()) {
attribute = "DIR";
} else {
size = f.length() + "";
attribute = f.canRead() ? "R" : " ";
attribute += f.canWrite() ? "W" : " ";
attribute += f.isHidden() ? "H" : " ";
}
System.out.printf("%s %3s %6s %s\n",
df.format(new Date(f.lastModified())), attribute, size, name);
}
}
}
현재 디렉토리에 속한 파일과 디렉토리의 이름과 크기 등 상세정보를 보여주는 예제다.
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
class FileEx5 {
public static void main(String[] args) {
if(args.length != 1 || args[0].length() != 1 || "tTlLnN".indexOf(args[0]) == -1) {
System.out.println("USAGE : java FileEx5 SORT_OPTION");
System.out.println("SORT_OPTION : ");
System.out.println("t Time ascending sort.");
System.out.println("T Time descending sort.");
System.out.println("l Length ascending sort.");
System.out.println("L Length descending sort.");
System.out.println("n Name ascending sort.");
System.out.println("N Name descending sort.");
System.exit(0);
}
final char option = args[0].charAt(0);
String currDir = System.getProperty("user.dir");
File dir = new File(currDir);
File[] files = dir.listFiles();
Comparator comp = new Comparator() {
public int compare(Object o1, Object o2) {
long time1 = ((File)o1).lastModified();
long time2 = ((File)o2).lastModified();
long length1 = ((File)o1).length();
long length2 = ((File)o2).length();
String name1 = ((File)o1).getName().toLowerCase();
String name2 = ((File)o2).getName().toLowerCase();
int result = 0;
switch (option) {
case 't':
if(time1 - time2 > 0) result = 1;
else if(time1 - time2 == 0) result = 0;
else if(time1 - time2 < 0) result = -1;
break;
case 'T':
if(time1 - time2 > 0) result = -1;
else if(time1 - time2 == 0) result = 0;
else if(time1 - time2 < 0) result = 1;
break;
case 'l':
if(length1 - length2 > 0) result = 1;
else if(length1 - length2 == 0) result = 0;
else if(length1 - length2 < 0) result = -1;
break;
case 'L':
if(length1 - length2 > 0) result = -1;
else if(length1 - length2 == 0) result = 0;
else if(length1 - length2 < 0) result = 1;
break;
case 'n':
result = name1.compareTo(name2);
break;
case'N':
result = name2.compareTo(name1);
break;
}
return result;
}
public boolean equals(Obejct o) { return false; }
};
Arrays.sort(files. comp);
for(int i=0; i<files.length; i++){
File f = files[i];
String name = f.getName();
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm");
String attribute = "";
String size = "";
if(files[i].isDirectory()) {
attribute = "DIR";
} else {
size = f.length() + "";
attribute = f.canRead() ? "R" : " ";
attribute += f.canWrite() ? "W" : " ";
attribute += f.isHidden() ? "H" : " ";
}
System.out.printf("%s %3s %6s %s\n",
df.format(new Date(f.lastModified())), attribute, size, name);
}
}
}
이전의 파일의 속성을 보여 주는 예제에 정렬기능을 추가한 예제이다. 시간이나 파일크기, 이름으로 오름차순 또는 내림차순으로 파일목록을 정렬하여 볼 수 있다.
import java.io.*;
class FileEx6 {
static int found = 0;
public static void main(String[] args) {
if(args.length != 2) {
System.out.println("USAGE : java FileEx6 DIRECTORT KEYWORD");
System.exit(0);
}
File dir = new File(args[0]);
String keyword = args[1];
if(!dir.exists() || !dir.isDirectory()) {
System.out.println("유효하지 않은 디렉토리 입니다.");
System.exit()
}
try {
findInFiles(dir, keyword);
} catch (IOException e) {
e.printStacktrace();0
}
System.out.println();
System.out.println("총 " + found + " 개의 라인에서 '" + keyword + "' 을/를 발견하였습니다.");
}
public static void findInFiles(File dir, String keyword) throws IOException {
File[] files = dir.listFiles();
for(int i=0; i< files.length; i++) {
if(files[i].isDirecrtory()) {
findInFiles(files[i], keyword);
} else {
String filename = files[i].getName();
String extension = filename.substring(filename.lastIndexOf(".") + 1);
extension = "," + extension + ",";
if(",java,txt,bak,".indexOf(extension) == -1) continue;
filename = dir.getAbsolutePath() + File.separator + filename;
FileReader fr = new FileReader(files[i]);
BufferedReader br = new BufferedReader(fr);
String data = "";
int lineNum = 0;
while((data=br.readLine()) != null) {
lineNum++;
if(data.indexOf(keyword) != -1){
found++;
System.out.println("[" + filename + "(" + lineNum + ")" + "]" + data);
}
}
br.close();
}
}
}
}
이전의 디렉토리에 포함된 파일의 목록 및 서브디렉토리의 목록을 출력한 것과 같이 재귀호출을 이용해서 지정한 디렉토리와 서브디렉토리에 포함된, 확장자가 'java', 'txt', 'bak'인 모든 파일의 내용을 읽어서 지정한 키워드가 포함된 라인을 출력하는 예제이다.
파일의 내용을 라인단위로 읽기 위해서 BufferedReader의 readLine()을 이용하였다.
extension = "," + extension + ",";if(",java,txt,bak,".indexOf(extension) == -1) continue;
if(",java,txt,bak,".indexOf(extension) == -1) continue;
구분자를 ',' 로 하여 확장자를 붙여서 문자열을 만든 다음, indexOf()를 이용해서 이 문자열에 호가장자가 포함되었는지 확인하고 없으면 넘어가도록 되어 있다. 확장자(extension)의 뒤쪽이나 앞족에만 구분자를 붙이면 확장자가 'ava'와 같이 부분적으로 일치하는 경우에 문제가 생긴다.
import java.io.*;
class FileEx7 {
public static void main(String[] args) {
if(args.length != 1) {
System.out.println("USAGE : java FileEx7 pattern");
System.exit(0);
}
String currDir = System.getProperty("user.dir");
File dir = new File(currDir);
final String pattern = args[0];
// String[] list(FilenameFilter filter)
String[] files = dir.list(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.indexOf(pattern) != -1;
}
});
for(int i=0; i<files.length; i++) {
System.out.println(files[i]);
}
}
}
FilenameFilter를 구현해서 String[] list(FilenameFilter filter)와 함께 사용해서 특정조건에 맞는 파일의 목록을 얻는 방법을 보여주는 예시이다.
FilenameFilter의 내용은 다음과 같이 accept 메서드 하나만 선언되어 있으며 이 메서드만 구현해 주면 된다.
public interface FilenameFilter {
boolean accept(File dir, String name);
}
import java.io.*;
class FileEx8 {
static int deletedFiles = 0;
public static void main(String[] args) {
if(args.length != 1) {
System.out.println("USAGE : java FileEx8 Extension");
System.exit(0);
}
String currDir = System.getProperty("user.dir");
File dir = new File(currDir);
String ext = "." + args[0];
delete(dir, ext);
System.out.println(deletedFiles + " 개의 파일이 삭제되었습니다.");
}
public static void delete(File dir, String ext) {
File[] files = dir.listFiles();
for(int i=0; i<files.length; i++) {
if(files[i].isDirectory()) {
delete(files[i], ext);
} else {
String filename = files[i].getAbsolutePath();
if(filename.endsWith(ext)) {
System.out.prin(filename);
if(files[i].delete()) {
System.out.println(" - 삭제 성공");
} else {
System.out.println(" - 삭제 실패");
}
}
}
}
}
}
이 예제 역시 재귀호출을 이용해서 지정된 디렉토리와 하위 디렉토이레 있는 파일 중에서 지정된 확장자를 가진 파일을 delete()를 호출해서 삭제한다. delete()는 해당 파일을 삭제하는데 성공하면 true를, 실패하면 false를 반환한다.
import java.io.*;
class FileEx9 {
public static void main(String[] args) {
if(args.length != 1) {
System.out.println("USAGE : java FileEx9 DIRECTORY");
System.exit(0);
}
File dir = new File(args[0]);
if(!dir.exists() || !dir.isDirecrtory()) {
System.out.println("유효하지 않은 디렉토리 입니다.");
System.exit(0);
}
File[] list = dir.listFiles();
for(int i=0; i<list.length; i++) {
String fileName = list[i].getName();
// 파일명
String newFileName = "0000" + fileName;
newFileName = newFileName.substring(newFileName.length() - 7);
list[i].renameTo(new File(dir, newFileName));
}
}
}
renameTo(File f)를 이용해서 파일의 이름을 바꾸는 간단한 예제이다. 여기서는 파일명이 숫자로 되어 있을 때 앞에 '0000'을 붙인 다음 substring()으로 이름의 길이를 맞춰주는 내용으로 작성하였다.
파일이름이 '1.jpg', '2,jpg' 와 같이 숫자로 되어 있는 경우, 파일이름으로 정렬하면 '1.jpg' 다음에 '2.jpg' 가 아닌 '11.jpg' 가 오게 된다. 이것을 바로 잡기위해 파일 이름 앞에 '0000'을 붙이면, 파일이름으로 저장하였을 때 '00001.jpg', 다음에 '00002.jpg'가 온다.
import java.io.*;
class FileSplit {
public static void main(String[] args) {
if(args.length < 2) {
System.out.println("USAGE : java FileSplit filename SIZE_KB");
System.exit(0);
}
final int VOLUME = Integer.parseInt(args[1]) * 1000;
try {
String filename = args[0];
FileInputStream fis = new FileInputStream(filename);
BufferedInputStream bis = new BufferedInputStream(fis);
FileOutputStream fos = null;
BufferedOutputStream bos = null;
int data = 0;
int i = 0;
int number = 0;
while((data = bis.read()) != -1) {
if(i%VOLUME==0) {
if(i!=0) {
bos.close();
}
fos = new FileOutputStream(filename + "_." + ++number);
bos = new BufferedOutputStream(fos);
}
bos.write(data);
i++;
}
bis.close();
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
지정한 파일을 지정한 크기로 잘라서 여러개의 파일로 만드는 예제이다. 다음 에제에서는 잘라진 파일들을 붙여서 다시 원래의 파일로 만들 것이다.
import java.io.*;
class FileMerge {
public static void main(String[] args) {
if(args.length < 2) {
System.out.println("USAGE : java FileMerge filename");
System.exit(0);
}
String mergeFilename = args[0];
try {
File tempFile = File.createTempFile("~mergetemp", ".tmp");
tempFile.deleteOnExit();
FileOutputStream fos = new FileOutputStream(tempFile);
BufferedOutputStream bos = new BufferedOutputStream(fos);
BufferedInputStream bis = null;
int number = 1;
File f = new File(mergeFilename = "_." + number);
while(f.exists()) {
f.setReadOnly();
bis = new BufferedInputStream(new FileInputStream(f));
int data = 0;
while((data = bis.read()) != -1) {
bos.write(data);
}
bis.close();
f = new File(mergeFilename + "_." ++number);
}
bos.close();
File oldFile = new File(mergeFilename);
if(oldFile.exists())
oldFile.delete();
tempFile.renameTo(oldFile);
} catch (IOException e) {}
}
}
이전 예제에서 나눈 파일을 다시 합치는 예제니다. 작업할 임시파일을 새로 만들고 프로그램 종료시 자동 삭제 되도록 했다. 프로그램의 실행도중에 사용자에 의해 중단되거나 했을 때, 파일이 합쳐지는 과정에서 생성된 불완전한 파일이 생성되는 것을 막기 위해서 임시파일을 사용하는 것이다.
파일을 합치는 작업을 온전히 마치고 나면, 기존 파일을 삭제하고 임시파일의 이름을 기존 파일의 이름으로 변경한다.
임시파일이 생성되는 곳은 createTempFile메서드에서 지정할 수도 있지만, 지정하지 않으면, 시스템 속성인 'java.io.tmpdir'에 지정된 디렉토리가 된다.
|참고| System.getProperty("java.io.tmpdir")를 출력해보면 임시 디렉토리의 위치를 확인할 수 있다.
작업을 마치고 나면 기존 파일을 삭제하고 임시파일의 이름을 기존 파일의 이름으로 변경한다.
'Java 길찾기 > Java의 정석' 카테고리의 다른 글
[Java] 직렬화가 가능한 클래스 만들기 - Serializable, transient (0) | 2022.08.02 |
---|---|
[Java] 직렬화(Serialization) (0) | 2022.08.01 |
[Java] 입출력 I/O - RandomAccessFile (0) | 2022.07.28 |
[Java] 입출력 I/O - 표준입출력의 대상변경(setOut(), setErr(), setIn()) (0) | 2022.07.27 |
[Java] 입출력 I/O - 표준입출력(System.in, System.out, System.err) (0) | 2022.07.26 |