はじめに

Javaプログラムでは、設定ファイルの読み込み、ログの出力、データの永続化など、ファイル操作が頻繁に必要になります。Java 7で導入されたNIO.2(New I/O 2) は、従来のjava.io.Fileクラスの課題を解決し、より直感的で強力なファイル操作APIを提供します。

この記事では、NIO.2の中核であるPathインターフェースとFilesクラスを使って、ファイルやディレクトリを効率的に操作する方法を解説します。

前提条件

この記事を読むにあたって、以下の知識があることを前提としています。

  • Javaの例外処理(try-catch文)
  • try-with-resources構文
  • 基本的なJavaの文法

実行環境

  • Java 21(LTS)
  • 任意のIDEまたはテキストエディタ

PathとFilesの基本

NIO.2でファイル操作を行う際の2つの重要なクラスを理解しましょう。

Pathインターフェースとは

Pathは、ファイルシステム内のファイルやディレクトリの場所(パス) を表すインターフェースです。従来のFileクラスと異なり、Pathはパス自体の操作に特化しており、実際のファイル操作はFilesクラスに委譲します。

Pathの生成方法

PathオブジェクトはPath.of()メソッドで生成します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import java.nio.file.Path;

public class PathBasics {
    public static void main(String[] args) {
        // 文字列からPathを生成
        Path path1 = Path.of("data/sample.txt");
        
        // 複数の文字列を結合してPathを生成
        Path path2 = Path.of("data", "logs", "app.log");
        
        // 絶対パスを指定
        Path path3 = Path.of("/home/user/documents/report.pdf");
        
        // Windowsの場合
        Path path4 = Path.of("C:\\Users\\user\\Documents\\report.pdf");
        
        System.out.println("path1: " + path1);
        System.out.println("path2: " + path2);
    }
}

Pathの主要メソッド

Pathインターフェースには、パスを操作・分析するための便利なメソッドが用意されています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.nio.file.Path;

public class PathMethods {
    public static void main(String[] args) {
        Path path = Path.of("/home/user/documents/report.pdf");
        
        // ファイル名を取得
        System.out.println("ファイル名: " + path.getFileName());
        // 出力: report.pdf
        
        // 親ディレクトリを取得
        System.out.println("親ディレクトリ: " + path.getParent());
        // 出力: /home/user/documents
        
        // ルート要素を取得
        System.out.println("ルート: " + path.getRoot());
        // 出力: /
        
        // パス要素の数を取得
        System.out.println("要素数: " + path.getNameCount());
        // 出力: 4
        
        // 絶対パスかどうか
        System.out.println("絶対パス: " + path.isAbsolute());
        // 出力: true
        
        // パスの正規化(..や.を解決)
        Path messyPath = Path.of("/home/user/../user/./documents");
        System.out.println("正規化: " + messyPath.normalize());
        // 出力: /home/user/documents
    }
}

パスの結合(resolve)

resolve()メソッドを使うと、ベースパスに別のパスを結合できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import java.nio.file.Path;

public class PathResolve {
    public static void main(String[] args) {
        Path baseDir = Path.of("/home/user/documents");
        
        // 相対パスを結合
        Path filePath = baseDir.resolve("reports/annual.pdf");
        System.out.println(filePath);
        // 出力: /home/user/documents/reports/annual.pdf
        
        // 兄弟パスへの変更(resolveSibling)
        Path siblingPath = filePath.resolveSibling("monthly.pdf");
        System.out.println(siblingPath);
        // 出力: /home/user/documents/reports/monthly.pdf
    }
}

Filesクラスとは

Filesクラスは、ファイルやディレクトリに対する実際の操作を行う静的メソッドを提供するユーティリティクラスです。読み込み、書き込み、コピー、削除、属性取得など、あらゆるファイル操作がこのクラスを通じて行えます。

ファイルの読み書き

テキストファイルを一括で読み込む(readString)

Java 11で追加されたFiles.readString()メソッドは、ファイル全体を一度に文字列として読み込みます。小〜中規模のファイルに最適です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.IOException;

public class ReadStringExample {
    public static void main(String[] args) {
        Path filePath = Path.of("sample.txt");
        
        try {
            // ファイル全体を文字列として読み込む(UTF-8がデフォルト)
            String content = Files.readString(filePath);
            System.out.println(content);
        } catch (IOException e) {
            System.err.println("ファイルの読み込みに失敗しました: " + e.getMessage());
        }
    }
}

文字コードを指定して読み込む

UTF-8以外の文字コードでエンコードされたファイルを読み込む場合は、Charsetを指定します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.io.IOException;

public class ReadStringWithCharset {
    public static void main(String[] args) {
        Path filePath = Path.of("shift_jis_file.txt");
        
        try {
            // Shift_JISで読み込む
            String content = Files.readString(filePath, Charset.forName("Shift_JIS"));
            System.out.println(content);
        } catch (IOException e) {
            System.err.println("ファイルの読み込みに失敗しました: " + e.getMessage());
        }
    }
}

行単位で読み込む(readAllLines)

ファイルを行のリストとして読み込む場合はFiles.readAllLines()を使用します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.IOException;
import java.util.List;

public class ReadAllLinesExample {
    public static void main(String[] args) {
        Path filePath = Path.of("data.csv");
        
        try {
            // 全行をリストとして取得
            List<String> lines = Files.readAllLines(filePath);
            
            for (int i = 0; i < lines.size(); i++) {
                System.out.println("行 " + (i + 1) + ": " + lines.get(i));
            }
        } catch (IOException e) {
            System.err.println("ファイルの読み込みに失敗しました: " + e.getMessage());
        }
    }
}

テキストファイルに書き込む(writeString)

Java 11で追加されたFiles.writeString()メソッドは、文字列をファイルに書き込みます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.IOException;

public class WriteStringExample {
    public static void main(String[] args) {
        Path filePath = Path.of("output.txt");
        String content = "Hello, Java NIO.2!\nファイルI/Oは簡単です。";
        
        try {
            // ファイルに書き込む(存在しなければ作成、存在すれば上書き)
            Files.writeString(filePath, content);
            System.out.println("ファイルを書き込みました");
        } catch (IOException e) {
            System.err.println("ファイルの書き込みに失敗しました: " + e.getMessage());
        }
    }
}

ファイルへの追記

既存ファイルに内容を追加する場合は、StandardOpenOption.APPENDオプションを指定します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.io.IOException;

public class AppendToFile {
    public static void main(String[] args) {
        Path filePath = Path.of("log.txt");
        String logEntry = "2026-01-03 10:00:00 - アプリケーション開始\n";
        
        try {
            // ファイルに追記(CREATE: なければ作成、APPEND: 末尾に追加)
            Files.writeString(filePath, logEntry, 
                StandardOpenOption.CREATE, 
                StandardOpenOption.APPEND);
            System.out.println("ログを追記しました");
        } catch (IOException e) {
            System.err.println("ファイルの書き込みに失敗しました: " + e.getMessage());
        }
    }
}

行のリストを書き込む

複数行をファイルに書き込む場合はFiles.write()メソッドを使用します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.IOException;
import java.util.List;

public class WriteLinesExample {
    public static void main(String[] args) {
        Path filePath = Path.of("names.txt");
        List<String> names = List.of("田中太郎", "鈴木花子", "佐藤一郎");
        
        try {
            // 行のリストをファイルに書き込む
            Files.write(filePath, names);
            System.out.println("ファイルを書き込みました");
        } catch (IOException e) {
            System.err.println("ファイルの書き込みに失敗しました: " + e.getMessage());
        }
    }
}

バッファリングによる大容量ファイル処理

数百MB以上の大容量ファイルを処理する場合、readString()readAllLines()はメモリを大量に消費します。このような場合は、バッファリングを使用して効率的に処理します。

BufferedReaderで読み込む

Files.newBufferedReader()で取得したBufferedReaderを使うと、ファイルを1行ずつ効率的に読み込めます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.BufferedReader;
import java.io.IOException;

public class BufferedReaderExample {
    public static void main(String[] args) {
        Path filePath = Path.of("large_file.txt");
        
        // try-with-resourcesで自動的にリソースをクローズ
        try (BufferedReader reader = Files.newBufferedReader(filePath)) {
            String line;
            int lineCount = 0;
            
            while ((line = reader.readLine()) != null) {
                lineCount++;
                // 処理(例: 特定のパターンを検索)
                if (line.contains("ERROR")) {
                    System.out.println("行 " + lineCount + ": " + line);
                }
            }
            
            System.out.println("総行数: " + lineCount);
        } catch (IOException e) {
            System.err.println("ファイルの読み込みに失敗しました: " + e.getMessage());
        }
    }
}

BufferedWriterで書き込む

大量のデータを書き込む場合はFiles.newBufferedWriter()を使用します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.BufferedWriter;
import java.io.IOException;

public class BufferedWriterExample {
    public static void main(String[] args) {
        Path filePath = Path.of("output_large.txt");
        
        try (BufferedWriter writer = Files.newBufferedWriter(filePath)) {
            for (int i = 1; i <= 100000; i++) {
                writer.write("行 " + i + ": データ内容\n");
            }
            System.out.println("大容量ファイルを書き込みました");
        } catch (IOException e) {
            System.err.println("ファイルの書き込みに失敗しました: " + e.getMessage());
        }
    }
}

バイナリファイルの読み書き

画像やPDFなどのバイナリファイルは、readAllBytes()write()で処理します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.IOException;

public class BinaryFileExample {
    public static void main(String[] args) {
        Path sourcePath = Path.of("image.png");
        Path destPath = Path.of("image_copy.png");
        
        try {
            // バイナリファイルを読み込む
            byte[] data = Files.readAllBytes(sourcePath);
            System.out.println("ファイルサイズ: " + data.length + " bytes");
            
            // バイナリファイルを書き込む
            Files.write(destPath, data);
            System.out.println("コピーが完了しました");
        } catch (IOException e) {
            System.err.println("ファイル操作に失敗しました: " + e.getMessage());
        }
    }
}

ディレクトリの作成・削除・一覧取得

ディレクトリの作成

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.IOException;

public class CreateDirectoryExample {
    public static void main(String[] args) {
        // 単一ディレクトリの作成
        Path dir = Path.of("new_directory");
        
        // ネストしたディレクトリの作成
        Path nestedDir = Path.of("parent/child/grandchild");
        
        try {
            // 単一ディレクトリを作成(親が存在しないとエラー)
            if (!Files.exists(dir)) {
                Files.createDirectory(dir);
                System.out.println("ディレクトリを作成しました: " + dir);
            }
            
            // ネストしたディレクトリを一括作成(mkdir -p 相当)
            Files.createDirectories(nestedDir);
            System.out.println("ネストディレクトリを作成しました: " + nestedDir);
            
        } catch (IOException e) {
            System.err.println("ディレクトリの作成に失敗しました: " + e.getMessage());
        }
    }
}

ディレクトリの内容を一覧取得

Files.list()メソッドでディレクトリ内のエントリを取得できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.IOException;
import java.util.stream.Stream;

public class ListDirectoryExample {
    public static void main(String[] args) {
        Path dir = Path.of(".");
        
        try (Stream<Path> stream = Files.list(dir)) {
            System.out.println("カレントディレクトリの内容:");
            stream.forEach(path -> {
                String type = Files.isDirectory(path) ? "[DIR]" : "[FILE]";
                System.out.println(type + " " + path.getFileName());
            });
        } catch (IOException e) {
            System.err.println("ディレクトリの一覧取得に失敗しました: " + e.getMessage());
        }
    }
}

サブディレクトリを含めた再帰的な一覧取得

Files.walk()メソッドを使うと、サブディレクトリを含めて再帰的にファイルを列挙できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.IOException;
import java.util.stream.Stream;

public class WalkDirectoryExample {
    public static void main(String[] args) {
        Path startDir = Path.of("src");
        
        try (Stream<Path> stream = Files.walk(startDir)) {
            System.out.println("全ファイル一覧:");
            stream.filter(Files::isRegularFile)
                  .forEach(System.out::println);
        } catch (IOException e) {
            System.err.println("ディレクトリ走査に失敗しました: " + e.getMessage());
        }
    }
}

深さを制限した走査

走査の深さを制限することも可能です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.IOException;
import java.util.stream.Stream;

public class WalkWithDepthExample {
    public static void main(String[] args) {
        Path startDir = Path.of(".");
        int maxDepth = 2; // 2階層まで
        
        try (Stream<Path> stream = Files.walk(startDir, maxDepth)) {
            stream.forEach(System.out::println);
        } catch (IOException e) {
            System.err.println("ディレクトリ走査に失敗しました: " + e.getMessage());
        }
    }
}

ファイルのコピー・移動・削除

ファイルのコピー

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.io.IOException;

public class CopyFileExample {
    public static void main(String[] args) {
        Path source = Path.of("original.txt");
        Path destination = Path.of("backup/original_copy.txt");
        
        try {
            // コピー先ディレクトリがなければ作成
            Files.createDirectories(destination.getParent());
            
            // ファイルをコピー(既存ファイルがあれば上書き)
            Files.copy(source, destination, StandardCopyOption.REPLACE_EXISTING);
            System.out.println("ファイルをコピーしました");
            
        } catch (IOException e) {
            System.err.println("コピーに失敗しました: " + e.getMessage());
        }
    }
}

コピーオプション

StandardCopyOptionで挙動をカスタマイズできます。

オプション 説明
REPLACE_EXISTING 既存ファイルを上書きする
COPY_ATTRIBUTES ファイル属性(更新日時など)もコピーする
ATOMIC_MOVE 移動操作をアトミックに行う(moveのみ)

ファイルの移動(リネーム)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.io.IOException;

public class MoveFileExample {
    public static void main(String[] args) {
        Path source = Path.of("temp/data.txt");
        Path destination = Path.of("archive/data_2026.txt");
        
        try {
            Files.createDirectories(destination.getParent());
            
            // ファイルを移動(リネーム)
            Files.move(source, destination, StandardCopyOption.REPLACE_EXISTING);
            System.out.println("ファイルを移動しました");
            
        } catch (IOException e) {
            System.err.println("移動に失敗しました: " + e.getMessage());
        }
    }
}

ファイルの削除

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.IOException;

public class DeleteFileExample {
    public static void main(String[] args) {
        Path filePath = Path.of("temp_file.txt");
        
        try {
            // ファイルを削除(存在しないとNoSuchFileException)
            Files.delete(filePath);
            System.out.println("ファイルを削除しました");
            
        } catch (IOException e) {
            System.err.println("削除に失敗しました: " + e.getMessage());
        }
    }
}

存在する場合のみ削除

ファイルが存在しない場合にエラーを避けたい場合はdeleteIfExists()を使用します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.IOException;

public class DeleteIfExistsExample {
    public static void main(String[] args) {
        Path filePath = Path.of("temp_file.txt");
        
        try {
            // 存在する場合のみ削除(存在しなくてもエラーにならない)
            boolean deleted = Files.deleteIfExists(filePath);
            
            if (deleted) {
                System.out.println("ファイルを削除しました");
            } else {
                System.out.println("ファイルは存在しませんでした");
            }
            
        } catch (IOException e) {
            System.err.println("削除に失敗しました: " + e.getMessage());
        }
    }
}

ディレクトリの削除

ディレクトリを削除する場合、空でないと削除できません。再帰的に削除するにはFiles.walk()と組み合わせます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.IOException;
import java.util.Comparator;
import java.util.stream.Stream;

public class DeleteDirectoryRecursively {
    public static void main(String[] args) {
        Path dirToDelete = Path.of("temp_directory");
        
        try (Stream<Path> stream = Files.walk(dirToDelete)) {
            // 深い階層から順に削除(逆順ソート)
            stream.sorted(Comparator.reverseOrder())
                  .forEach(path -> {
                      try {
                          Files.delete(path);
                          System.out.println("削除: " + path);
                      } catch (IOException e) {
                          System.err.println("削除失敗: " + path);
                      }
                  });
            System.out.println("ディレクトリを再帰的に削除しました");
        } catch (IOException e) {
            System.err.println("ディレクトリ走査に失敗しました: " + e.getMessage());
        }
    }
}

ファイル属性の取得

基本的なファイル情報

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.IOException;

public class FileInfoExample {
    public static void main(String[] args) {
        Path filePath = Path.of("sample.txt");
        
        try {
            // ファイルの存在確認
            System.out.println("存在: " + Files.exists(filePath));
            
            // ファイルかディレクトリか
            System.out.println("ファイル: " + Files.isRegularFile(filePath));
            System.out.println("ディレクトリ: " + Files.isDirectory(filePath));
            
            // ファイルサイズ(バイト)
            System.out.println("サイズ: " + Files.size(filePath) + " bytes");
            
            // 読み取り・書き込み・実行権限
            System.out.println("読取可能: " + Files.isReadable(filePath));
            System.out.println("書込可能: " + Files.isWritable(filePath));
            System.out.println("実行可能: " + Files.isExecutable(filePath));
            
            // 最終更新日時
            System.out.println("最終更新: " + Files.getLastModifiedTime(filePath));
            
        } catch (IOException e) {
            System.err.println("ファイル情報の取得に失敗しました: " + e.getMessage());
        }
    }
}

詳細なファイル属性を取得

Files.readAttributes()で、より詳細なファイル属性を一括取得できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.io.IOException;

public class FileAttributesExample {
    public static void main(String[] args) {
        Path filePath = Path.of("sample.txt");
        
        try {
            // 基本ファイル属性を一括取得
            BasicFileAttributes attrs = Files.readAttributes(
                filePath, BasicFileAttributes.class);
            
            System.out.println("作成日時: " + attrs.creationTime());
            System.out.println("最終アクセス: " + attrs.lastAccessTime());
            System.out.println("最終更新: " + attrs.lastModifiedTime());
            System.out.println("サイズ: " + attrs.size() + " bytes");
            System.out.println("ディレクトリ: " + attrs.isDirectory());
            System.out.println("通常ファイル: " + attrs.isRegularFile());
            System.out.println("シンボリックリンク: " + attrs.isSymbolicLink());
            
        } catch (IOException e) {
            System.err.println("属性の取得に失敗しました: " + e.getMessage());
        }
    }
}

MIMEタイプの判定

ファイルのMIMEタイプ(コンテンツタイプ)を判定できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.IOException;

public class ProbeContentTypeExample {
    public static void main(String[] args) {
        Path[] files = {
            Path.of("document.pdf"),
            Path.of("image.png"),
            Path.of("data.json"),
            Path.of("style.css")
        };
        
        for (Path file : files) {
            try {
                String mimeType = Files.probeContentType(file);
                System.out.println(file.getFileName() + " -> " + mimeType);
            } catch (IOException e) {
                System.err.println("MIMEタイプの判定に失敗: " + file);
            }
        }
    }
}

Stream APIとの連携

NIO.2のFilesクラスは、Java 8で導入されたStream APIと組み合わせて強力なファイル処理が可能です。

Files.lines()でファイルを行ストリームとして処理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.IOException;
import java.util.stream.Stream;

public class FileLinesStreamExample {
    public static void main(String[] args) {
        Path filePath = Path.of("access.log");
        
        try (Stream<String> lines = Files.lines(filePath)) {
            // ERRORを含む行を抽出してカウント
            long errorCount = lines
                .filter(line -> line.contains("ERROR"))
                .peek(System.out::println)
                .count();
            
            System.out.println("エラー件数: " + errorCount);
        } catch (IOException e) {
            System.err.println("ファイルの読み込みに失敗しました: " + e.getMessage());
        }
    }
}

ファイルの検索(Files.find)

特定の条件に一致するファイルを検索できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.IOException;
import java.util.stream.Stream;

public class FindFilesExample {
    public static void main(String[] args) {
        Path startDir = Path.of("src");
        
        try (Stream<Path> stream = Files.find(startDir, 10,
            (path, attrs) -> 
                attrs.isRegularFile() && 
                path.toString().endsWith(".java"))) {
            
            System.out.println("Javaファイル一覧:");
            stream.forEach(System.out::println);
            
        } catch (IOException e) {
            System.err.println("ファイル検索に失敗しました: " + e.getMessage());
        }
    }
}

CSVファイルのデータ処理例

Stream APIを活用したCSVファイルの処理例を示します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.IOException;
import java.util.stream.Stream;

public class CsvProcessingExample {
    public static void main(String[] args) {
        Path csvPath = Path.of("sales.csv");
        
        try (Stream<String> lines = Files.lines(csvPath)) {
            // ヘッダーをスキップし、売上金額の合計を計算
            double totalSales = lines
                .skip(1) // ヘッダー行をスキップ
                .map(line -> line.split(","))
                .filter(columns -> columns.length >= 3)
                .mapToDouble(columns -> {
                    try {
                        return Double.parseDouble(columns[2]); // 3列目が売上
                    } catch (NumberFormatException e) {
                        return 0.0;
                    }
                })
                .sum();
            
            System.out.printf("売上合計: %.2f円%n", totalSales);
            
        } catch (IOException e) {
            System.err.println("CSVの処理に失敗しました: " + e.getMessage());
        }
    }
}

ディレクトリ内の大きなファイルを特定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.IOException;
import java.util.stream.Stream;

public class FindLargeFilesExample {
    public static void main(String[] args) {
        Path startDir = Path.of(".");
        long threshold = 1024 * 1024; // 1MB以上
        
        try (Stream<Path> stream = Files.walk(startDir)) {
            System.out.println("1MB以上のファイル:");
            stream.filter(Files::isRegularFile)
                  .filter(path -> {
                      try {
                          return Files.size(path) > threshold;
                      } catch (IOException e) {
                          return false;
                      }
                  })
                  .forEach(path -> {
                      try {
                          long size = Files.size(path);
                          System.out.printf("%s (%.2f MB)%n", 
                              path, size / (1024.0 * 1024.0));
                      } catch (IOException e) {
                          // 無視
                      }
                  });
        } catch (IOException e) {
            System.err.println("ディレクトリ走査に失敗しました: " + e.getMessage());
        }
    }
}

まとめ

この記事では、Java NIO.2のPathインターフェースとFilesクラスを使ったファイル操作について解説しました。

学んだ内容

トピック 主なメソッド
Pathの生成と操作 Path.of(), resolve(), getFileName(), getParent()
テキストファイルの読み込み Files.readString(), Files.readAllLines()
テキストファイルの書き込み Files.writeString(), Files.write()
大容量ファイル処理 Files.newBufferedReader(), Files.newBufferedWriter()
ディレクトリ操作 Files.createDirectory(), Files.list(), Files.walk()
コピー・移動・削除 Files.copy(), Files.move(), Files.delete()
ファイル属性 Files.size(), Files.getLastModifiedTime(), Files.readAttributes()
Stream API連携 Files.lines(), Files.find(), Files.walk()

ベストプラクティス

  1. try-with-resourcesを使用する: BufferedReaderやStreamなど、リソースを使用する場合は必ず自動クローズ
  2. 小さなファイルには便利メソッドを使う: readString()writeString()は簡潔で読みやすい
  3. 大きなファイルにはバッファリング: メモリ効率を考慮してBufferedReader/BufferedWriterを使用
  4. 存在確認を適切に行う: Files.exists()で事前チェック、またはdeleteIfExists()などの安全なメソッドを使用
  5. Streamは閉じる: Files.lines()Files.walk()が返すStreamは必ずクローズ

参考リンク