こんにちは!バックエンドエンジニアのキョンです。今回は、開発現場で頻繁に使用する「ファイル入出力(File I/O)」について、実務での経験を基に詳しく解説していきます。
目次
1. ファイル入出力の基本
File I/Oの基礎
入社当時の私は「FileInputStreamって何?BufferedReaderは?」と混乱していました。 今回はそんな疑問を解消していきましょう。
基本的なファイル操作
public class BasicFileOperations {
public static void main(String[] args) {
// ファイルオブジェクトの作成
File file = new File("test.txt");
// ファイルの存在確認
if (file.exists()) {
System.out.println("ファイルサイズ: " + file.length());
System.out.println("最終更新日時: " + new Date(file.lastModified()));
}
// ディレクトリ作成
File dir = new File("mydir");
dir.mkdir();
}
}
2. 様々な入出力方法
テキストファイルの読み書き
実務でよく使用する基本的なパターンです:
public class TextFileHandler {
// ファイル読み込み
public List<String> readLines(String filePath) {
List<String> lines = new ArrayList<>();
try (BufferedReader br = new BufferedReader(
new FileReader(filePath))) {
String line;
while ((line = br.readLine()) != null) {
lines.add(line);
}
} catch (IOException e) {
logger.error("ファイル読み込みエラー: " + filePath, e);
}
return lines;
}
// ファイル書き込み
public void writeLines(String filePath, List<String> lines) {
try (BufferedWriter bw = new BufferedWriter(
new FileWriter(filePath))) {
for (String line : lines) {
bw.write(line);
bw.newLine();
}
} catch (IOException e) {
logger.error("ファイル書き込みエラー: " + filePath, e);
}
}
}
バイナリファイルの処理
画像ファイルなどのバイナリデータを扱う例:
public class BinaryFileHandler {
public void copyFile(String sourcePath, String targetPath) {
try (FileInputStream fis = new FileInputStream(sourcePath);
FileOutputStream fos = new FileOutputStream(targetPath)) {
byte[] buffer = new byte[8192]; // 8KBのバッファ
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
logger.error("ファイルコピーエラー", e);
}
}
}
3. 実践的なファイル操作
CSVファイルの処理
実際のプロジェクトでよく使用するCSV処理の例:
public class CsvProcessor {
public List<User> readUsersCsv(String filePath) {
List<User> users = new ArrayList<>();
try (CSVReader reader = new CSVReader(new FileReader(filePath))) {
String[] header = reader.readNext(); // ヘッダーをスキップ
String[] line;
while ((line = reader.readNext()) != null) {
users.add(new User(
line[0], // ID
line[1], // 名前
Integer.parseInt(line[2]) // 年齢
));
}
} catch (IOException | CsvException e) {
logger.error("CSV読み込みエラー", e);
}
return users;
}
public void writeUsersCsv(String filePath, List<User> users) {
try (CSVWriter writer = new CSVWriter(new FileWriter(filePath))) {
// ヘッダー書き込み
writer.writeNext(new String[]{"ID", "Name", "Age"});
// データ書き込み
for (User user : users) {
writer.writeNext(new String[]{
user.getId(),
user.getName(),
String.valueOf(user.getAge())
});
}
} catch (IOException e) {
logger.error("CSV書き込みエラー", e);
}
}
}
大容量ファイルの処理
メモリを効率的に使用する例:
public class LargeFileProcessor {
public void processLargeFile(String filePath) {
try (Stream<String> lines = Files.lines(Paths.get(filePath))) {
lines.filter(line -> !line.isEmpty())
.map(this::processLine)
.forEach(this::saveResult);
} catch (IOException e) {
logger.error("大容量ファイル処理エラー", e);
}
}
private ProcessedData processLine(String line) {
// 1行ずつ処理
return new ProcessedData(line);
}
}
4. パフォーマンス最適化
バッファリングの活用
public class OptimizedFileIO {
private static final int BUFFER_SIZE = 8192;
public void copyLargeFile(String source, String target) {
try (BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(source), BUFFER_SIZE);
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(target), BUFFER_SIZE)) {
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
logger.error("ファイルコピーエラー", e);
}
}
}
5. よくあるトラブルと対策
リソースリークの防止
// 悪い例
public class BadExample {
public void badRead(String filePath) {
FileReader fr = null;
try {
fr = new FileReader(filePath);
// 処理
} catch (IOException e) {
logger.error("エラー", e);
} finally {
try {
if (fr != null) {
fr.close();
}
} catch (IOException e) {
logger.error("クローズエラー", e);
}
}
}
// 良い例
public void goodRead(String filePath) {
try (FileReader fr = new FileReader(filePath)) {
// 処理
} catch (IOException e) {
logger.error("エラー", e);
}
}
}
まとめ
ファイル入出力は基本に忠実に、かつ適切な方法を選択することが重要です。
私の経験から、以下のポイントを意識すると良いでしょう:
- try-with-resourcesを積極的に使用する
- 適切なバッファサイズを設定する
- 大容量ファイルはストリーム処理を活用する
- 例外処理を適切に行う
さらなる学習のために
- Java I/O & NIO APIドキュメント
- Java Performance Tuning
- アプリケーションのログ設計パターン
次回は「Javaでのログ出力」について解説する予定です。お楽しみに!
コメント