チュートリアル

C#実践編:ファイル操作

C#ファイル操作実践
広告エリア

はじめに

C#実践編では、実際の開発で必要になるスキルを学びます。第1回はファイル操作です。

C#ではSystem.IO名前空間のクラスを使用します。

テキストファイルの読み書き

ファイルを読む

using System.Text;

// ファイル全体を読み込む
string content = File.ReadAllText("sample.txt", Encoding.UTF8);
Console.WriteLine(content);

// 全行を配列として取得
string[] lines = File.ReadAllLines("sample.txt", Encoding.UTF8);
foreach (var line in lines)
{
    Console.WriteLine(line);
}

// 1行ずつ読み込み(大容量ファイル向け)
foreach (var line in File.ReadLines("sample.txt", Encoding.UTF8))
{
    Console.WriteLine(line);
}

// 非同期読み込み
string asyncContent = await File.ReadAllTextAsync("sample.txt", Encoding.UTF8);

ファイルに書き込む

using System.Text;

// 新規作成・上書き
File.WriteAllText("output.txt", "こんにちは\nC#\n", Encoding.UTF8);

// 追記
File.AppendAllText("output.txt", "追加の行\n", Encoding.UTF8);

// 複数行を書き込み
string[] lines = ["1行目", "2行目", "3行目"];
File.WriteAllLines("output.txt", lines, Encoding.UTF8);

// 非同期書き込み
await File.WriteAllTextAsync("output.txt", "内容", Encoding.UTF8);

usingステートメントによるリソース管理

ストリームは自動的に閉じられます。

using System.Text;

// 良い例(自動でDispose)
using (var reader = new StreamReader("file.txt", Encoding.UTF8))
{
    string? line;
    while ((line = reader.ReadLine()) != null)
    {
        Console.WriteLine(line);
    }
}
// ここでreaderは自動的に閉じられる

// C# 8.0以降のusing宣言
using var writer = new StreamWriter("output.txt", false, Encoding.UTF8);
writer.WriteLine("1行目");
writer.WriteLine("2行目");
// スコープを抜けると自動でDispose

StreamReader/StreamWriter

大容量ファイルの処理にはストリームが効果的です。

using System.Text;

// 読み込み
using var reader = new StreamReader("large.txt", Encoding.UTF8);
string? line;
while ((line = reader.ReadLine()) != null)
{
    ProcessLine(line);
}

// 書き込み
using var writer = new StreamWriter("output.txt", append: false, Encoding.UTF8);
foreach (var item in data)
{
    writer.WriteLine(item);
}

// StreamWriterのオプション
using var writerWithOptions = new StreamWriter(
    path: "output.txt",
    append: true,
    encoding: Encoding.UTF8,
    bufferSize: 4096);

FileStream

バイナリデータやより細かい制御が必要な場合に使用します。

// FileStreamの基本
using var stream = new FileStream(
    "file.bin",
    FileMode.Create,
    FileAccess.Write);
byte[] data = [1, 2, 3, 4, 5];
stream.Write(data);

// FileModeの種類
// Create     - 新規作成(既存は上書き)
// CreateNew  - 新規作成(既存があるとエラー)
// Open       - 既存を開く
// OpenOrCreate - 開くか新規作成
// Append     - 追記モード
// Truncate   - 既存を空にして開く

// FileAccessの種類
// Read, Write, ReadWrite

JSONファイルの操作

System.Text.Jsonを使用します(.NET Core 3.0+)。

using System.Text.Json;

// シリアライズ設定
var options = new JsonSerializerOptions
{
    WriteIndented = true,
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
    Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};

// JSONを読み込む
string json = File.ReadAllText("user.json", Encoding.UTF8);
User? user = JsonSerializer.Deserialize<User>(json, options);

// JSONに書き込む
var newUser = new User("太郎", 25, ["C#", "Unity"]);
string outputJson = JsonSerializer.Serialize(newUser, options);
File.WriteAllText("output.json", outputJson, Encoding.UTF8);

// 非同期処理
await using var stream = File.OpenRead("user.json");
User? asyncUser = await JsonSerializer.DeserializeAsync<User>(stream, options);

record User(string Name, int Age, List<string> Skills);

Newtonsoft.Json(従来の方法)

using Newtonsoft.Json;

// JSONを読み込む
string json = File.ReadAllText("user.json");
User? user = JsonConvert.DeserializeObject<User>(json);

// JSONに書き込む
var newUser = new User("太郎", 25, new List<string> { "C#" });
string outputJson = JsonConvert.SerializeObject(newUser, Formatting.Indented);
File.WriteAllText("output.json", outputJson);

CSVファイルの操作

シンプルなCSV処理の例です。

using System.Text;

// CSV読み込み
List<Dictionary<string, string>> ReadCsv(string path)
{
    var lines = File.ReadAllLines(path, Encoding.UTF8);
    if (lines.Length == 0) return [];

    var headers = lines[0].Split(',');
    var result = new List<Dictionary<string, string>>();

    for (int i = 1; i < lines.Length; i++)
    {
        var values = lines[i].Split(',');
        var row = new Dictionary<string, string>();
        for (int j = 0; j < headers.Length; j++)
        {
            row[headers[j]] = j < values.Length ? values[j] : "";
        }
        result.Add(row);
    }

    return result;
}

// CSV書き込み
void WriteCsv(string path, List<Dictionary<string, string>> data)
{
    if (data.Count == 0) return;

    var headers = data[0].Keys.ToList();
    var lines = new List<string>
    {
        string.Join(",", headers)
    };

    foreach (var row in data)
    {
        var values = headers.Select(h => row.GetValueOrDefault(h, ""));
        lines.Add(string.Join(",", values));
    }

    File.WriteAllLines(path, lines, Encoding.UTF8);
}

パス操作

// パスの結合
string path = Path.Combine("documents", "report.txt");
Console.WriteLine(path);  // documents\report.txt (Windows)

// パス情報の取得
string filePath = @"C:\Users\user\documents\report.txt";
Console.WriteLine(Path.GetFileName(filePath));      // report.txt
Console.WriteLine(Path.GetFileNameWithoutExtension(filePath));  // report
Console.WriteLine(Path.GetExtension(filePath));     // .txt
Console.WriteLine(Path.GetDirectoryName(filePath)); // C:\Users\user\documents

// 絶対パスに変換
string absolute = Path.GetFullPath("file.txt");
Console.WriteLine(absolute);

// 一時ファイル
string tempPath = Path.GetTempPath();
string tempFile = Path.GetTempFileName();

ディレクトリ操作

// 存在確認
Console.WriteLine(File.Exists("sample.txt"));
Console.WriteLine(Directory.Exists("folder"));

// ファイル情報
var fileInfo = new FileInfo("sample.txt");
Console.WriteLine(fileInfo.Length);          // バイト数
Console.WriteLine(fileInfo.CreationTime);    // 作成日時
Console.WriteLine(fileInfo.LastWriteTime);   // 更新日時

// ディレクトリ作成
Directory.CreateDirectory("new_folder");
Directory.CreateDirectory(@"a\b\c");  // 再帰的に作成

// ディレクトリ内のファイル一覧
string[] files = Directory.GetFiles(".");
string[] dirs = Directory.GetDirectories(".");

// パターンで検索
string[] txtFiles = Directory.GetFiles(".", "*.txt");

// 再帰的に検索
string[] allCsFiles = Directory.GetFiles(".", "*.cs", SearchOption.AllDirectories);

// EnumerateFiles(大量ファイル向け)
foreach (var file in Directory.EnumerateFiles(".", "*.txt"))
{
    Console.WriteLine(file);
}

// ファイル削除
File.Delete("file.txt");

// ディレクトリ削除
Directory.Delete("empty_folder");
Directory.Delete("folder", recursive: true);

// コピーと移動
File.Copy("src.txt", "dst.txt", overwrite: true);
File.Move("old.txt", "new.txt", overwrite: true);
Directory.Move("old_dir", "new_dir");

実践例:ログファイル処理

using System.Text;

record ErrorEntry(int Line, string Content);

List<ErrorEntry> ParseLogFile(string logPath)
{
    var errors = new List<ErrorEntry>();
    int lineNum = 0;

    foreach (var line in File.ReadLines(logPath, Encoding.UTF8))
    {
        lineNum++;
        if (line.Contains("ERROR"))
        {
            errors.Add(new ErrorEntry(lineNum, line.Trim()));
        }
    }

    return errors;
}

void SaveErrorReport(List<ErrorEntry> errors, string outputPath)
{
    using var writer = new StreamWriter(outputPath, false, Encoding.UTF8);

    writer.WriteLine($"エラーレポート - {DateTime.Now}");
    writer.WriteLine(new string('=', 50));
    writer.WriteLine();

    foreach (var error in errors)
    {
        writer.WriteLine($"行 {error.Line}: {error.Content}");
    }

    writer.WriteLine();
    writer.WriteLine($"合計: {errors.Count}件のエラー");
}

// 使用例
var errors = ParseLogFile("app.log");
SaveErrorReport(errors, "error_report.txt");

非同期ファイル操作

// 非同期読み込み
string content = await File.ReadAllTextAsync("file.txt", Encoding.UTF8);
string[] lines = await File.ReadAllLinesAsync("file.txt", Encoding.UTF8);

// 非同期書き込み
await File.WriteAllTextAsync("file.txt", "content", Encoding.UTF8);
await File.WriteAllLinesAsync("file.txt", lines, Encoding.UTF8);

// StreamReader/Writerの非同期メソッド
using var reader = new StreamReader("file.txt");
string? line = await reader.ReadLineAsync();

using var writer = new StreamWriter("file.txt");
await writer.WriteLineAsync("content");

まとめ

  • Fileクラスで簡単なファイル操作
  • StreamReader/StreamWriterで大容量ファイル処理
  • usingステートメントで安全なリソース管理
  • System.Text.JsonでJSON操作
  • Pathクラスでパス操作
  • Directoryクラスでディレクトリ操作

次回は例外処理について学びます。

広告エリア