MENU

Javaのファイル入出力を徹底解説!現役エンジニアが教えるFile I/Oの全て

こんにちは!バックエンドエンジニアのキョンです。今回は、開発現場で頻繁に使用する「ファイル入出力(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);
        }
    }
}

まとめ

ファイル入出力は基本に忠実に、かつ適切な方法を選択することが重要です。

私の経験から、以下のポイントを意識すると良いでしょう:

  1. try-with-resourcesを積極的に使用する
  2. 適切なバッファサイズを設定する
  3. 大容量ファイルはストリーム処理を活用する
  4. 例外処理を適切に行う

さらなる学習のために

  • Java I/O & NIO APIドキュメント
  • Java Performance Tuning
  • アプリケーションのログ設計パターン

次回は「Javaでのログ出力」について解説する予定です。お楽しみに!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次