금일은 File, Files 클래스에 대해 알아보는 시간입니다.
파일 또는 디렉토리를다룰 때는 File, Files, Path 클래스를 활용하면 됩니다.
File file = new File("temp/example.txt");
File directory = new File("temp/exampleDir");
// 1. exists(): 파일이나 디렉토리의 존재 여부를 확인
System.out.println("File exists: " + file.exists());
// 2. createNewFile(): 새 파일을 생성
boolean created = file.createNewFile();
System.out.println("File created: " + created);
// 3. mkdir(): 새 디렉토리를 생성
boolean dirCreated = directory.mkdir();
System.out.println("Directory created: " + dirCreated);
...
다음과 같이 메서드를 사용하면 되지만 오래된 방법입니다.
자바 1.0에서 File 클래스가 등장했다. 이후에 자바 1.7에서 File 클래스를 대체할 Files 와 Path 가 등장했다.
FileInputStream , FileWriter의 사용을 고민하기 전에 Files 에 있는 기능을 알아보고 필요한 함수는 찾아보면서 적용하는게 맞다.
Files 를 사용할 때 파일이나, 디렉토리의 경로는 Path 클래스를 사용해야 한다.
Path file = Path.of("temp/example.txt");
Path directory = Path.of("temp/exampleDir");
// 1. exists(): 파일이나 디렉토리의 존재 여부를 확인
System.out.println("File exists: " + Files.exists(file));
// 2. createFile(): 새 파일을 생성
try {
Files.createFile(file);
System.out.println("File created");
} catch (FileAlreadyExistsException e) {
System.out.println(file + " File already exists");
}
// 3. createDirectory(): 새 디렉토리를 생성
try {
Files.createDirectory(directory);
System.out.println("Directory created");
} catch (FileAlreadyExistsException e) {
System.out.println(directory + " Directory already exists");
}
// 4. delete(): 파일이나 디렉토리를 삭제
//Files.delete(file);
//System.out.println("File deleted");
// 5. isRegularFile(): 일반 파일인지 확인
System.out.println("Is regular file: " + Files.isRegularFile(file));
// 6. isDirectory(): 디렉토리인지 확인
System.out.println("Is directory: " + Files.isDirectory(directory));
// 7. getFileName(): 파일이나 디렉토리의 이름을 반환
System.out.println("File name: " + file.getFileName());
// 8. size(): 파일의 크기를 바이트 단위로 반환
System.out.println("File size: " + Files.size(file) + " bytes");
// 9. move(): 파일의 이름을 변경하거나 이동
Path newFile = Paths.get("temp/newExample.txt");
Files.move(file, newFile, StandardCopyOption.REPLACE_EXISTING);
System.out.println("File moved/renamed");
// 10. getLastModifiedTime(): 마지막으로 수정된 시간을 반환
System.out.println("Last modified: " + Files.getLastModifiedTime(newFile));
// 추가: readAttributes(): 파일의 기본 속성들을 한 번에 읽기
BasicFileAttributes attrs = Files.readAttributes(newFile, BasicFileAttributes.class);
System.out.println("===== Attributes =====");
System.out.println("Creation time: " + attrs.creationTime());
System.out.println("Is directory: " + attrs.isDirectory());
System.out.println("Is regular file: " + attrs.isRegularFile());
System.out.println("Is symbolic link: " + attrs.isSymbolicLink());
System.out.println("Size: " + attrs.size());
File 의 경로 표시 관련 메서드 입니다.
File file = new File("temp/..");
System.out.println("path = " + file.getPath());
// 절대 경로
System.out.println("Absolute path = " + file.getAbsolutePath());
// 정규 경로
System.out.println("Canonical path = " + file.getCanonicalPath());
File[] files = file.listFiles();
for (File f : files) {
System.out.println( (f.isFile() ? "F" : "D") + " | " + f.getName());
}
path = temp/..
Absolute path = /Users/yh/study/inflearn/java/java-adv2/temp/..
Canonical path = /Users/yh/study/inflearn/java/java-adv2
D | temp
F | java-adv2-v1.iml
D | out
F | .gitignore
D | .idea
D | src
절대 경로(Absolute path): 절대 경로는 경로의 처음부터 내가 입력한 모든 경로를 다 표현한다.
정규 경로(Canonical path): 경로의 계산이 모두 끝난 경로이다. 정규 경로는 하나만 존재한다.
- .. 는 상위 디렉토리를 듯한다.
다음은 Files, Path 의 경로 표시이다.
public static void main(String[] args) throws IOException {
Path path = Path.of("temp/..");
System.out.println("path = " + path);
// 절대 경로
System.out.println("Absolute path = " + path.toAbsolutePath());
// 정규 경로
System.out.println("Canonical path = " + path.toRealPath());
Stream<Path> pathStream = Files.list(path);
List<Path> list = pathStream.toList();
pathStream.close();
for (Path p : list) {
System.out.println( (Files.isRegularFile(p) ? "F" : "D") + " | " + p.getFileName());
}
}
문자로된 파일을 읽고 쓸 때 과거에는 FileReader , FileWriter 같은 복잡한 스트림 클래스를 사용해야 하며 한 줄 단위로 파일
을 읽으려면 BufferedReader 와 같은 스트림 클래스를 추가해야 했다. 이러한 문제를 Files 클래스가 해결해준다.
모든 문자 읽기
public static void main(String[] args) throws IOException {
String writeString = "abc\n가나다";
System.out.println("== Write String ==");
System.out.println(writeString);
Path path = Path.of(PATH);
// 파일에 쓰기
Files.writeString(path, writeString, UTF_8);
// 파일에서 읽기
String readString = Files.readString(path, UTF_8);
System.out.println("== Read String ==");
System.out.println(readString);
}
한줄 씩 읽기
// 파일에 쓰기
Files.writeString(path, writeString, UTF_8);
// 파일에서 읽기
System.out.println("== Read String ==");
List<String> lines = Files.readAllLines(path, UTF_8);
for (int i = 0; i < lines.size(); i++) {
System.out.println((i + 1) + ": " + lines.get(i));
}
파일을 스트림 단위로 나누어 조회 하면 용량이 큰 파일의 경우 outOfMemoryException 에러를 피할수 있다.
메모리 사용량을 줄인 람다와 스트림 코드이다.
Stream<String> lineStream = Files.lines(path, UTF_8);
try(Stream<String> lineStream = Files.lines(path, UTF_8)){
lineStream.forEach(line -> System.out.println(line));
}
예시로 200mb 임시 파일을 하나 만들어보겠습니다.
public static void main(String[] args) throws IOException {
String fileName = "temp/copy.dat";
long startTime = System.currentTimeMillis();
FileOutputStream fos = new FileOutputStream(fileName);
byte[] buffer = new byte[FILE_SIZE];
fos.write(buffer);
fos.close();
long endTime = System.currentTimeMillis();
System.out.println("File created: " + fileName);
System.out.println("File size: " + FILE_SIZE / 1024 / 1024 + "MB");
System.out.println("Time taken: " + (endTime - startTime) + "ms");
}
public static void main(String[] args) throws IOException {
long startTime = System.currentTimeMillis();
Path source = Path.of("temp/copy.dat");
Path target = Path.of("temp/copy_new.dat");
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
long endTime = System.currentTimeMillis();
System.out.println("Time taken: " + (endTime - startTime) + "ms");
}
Files.copy() 는 자바에 파일 데이터를 불러오지 않고, 운영체제의 파일 복사 기능을 사용한다. 파일에서의 byte 코드 즉 자바 메모리의 불러오는 과정이 불필요하다 .
물론 이 기능은 파일에서 파일을 복사할 때만 유용하다. 만약 파일의 정보를 읽어서 처리해야 하거나, 스트림을 통해 네
트워크에 전달해야 한다면 앞서 설명한 스트림을 직접 사용해야 한다.