メモ

# CSV分割・変換ツール 設計書

## 概要

このツールは、コマンドライン引数で指定されたCSVファイルを4つの出力ファイルに分割し、各ファイルごとに異なる変換仕様に従って出力する。変換内容には単純な値変換、固定値出力、条件付きスキップ、2段階抽出などが含まれる。

---

## 📁 クラス構成

```
├── Program.cs             // エントリポイント
├── CsvRow.cs              // 1行分のCSVデータ表現（ラッパー）
├── CsvLoader.cs           // CSV読み込みロジック
├── CsvSplitter.cs         // 各ファイル向けの抽出処理
├── Converter.cs           // 値の変換処理
├── Extractor.cs           // ファイルごとの行抽出処理
└── Writer.cs              // 分割後CSVの出力処理
```

---

## 🧩 クラス詳細

### CsvRow.cs

```csharp
public class CsvRow
{
    private readonly List<string> _columns;

    public CsvRow(List<string> columns)
    {
        _columns = columns;
    }

    public string this[int index] => index < _columns.Count ? _columns[index] : "";

    public List<string> Columns => _columns;
}
```

---

### CsvLoader.cs

```csharp
public static class CsvLoader
{
    public static List<CsvRow> Load(string path)
    {
        var rows = new List<CsvRow>();
        foreach (var line in File.ReadLines(path))
        {
            var columns = line.Split(',').ToList();
            rows.Add(new CsvRow(columns));
        }
        return rows;
    }
}
```

---

### CsvSplitter.cs

```csharp
public static class CsvSplitter
{
    public static Dictionary<string, List<CsvRow>> Split(List<CsvRow> rows, List<Func<CsvRow, CsvRow>> extractors, List<string> fileNames)
    {
        var result = new Dictionary<string, List<CsvRow>>();
        for (int i = 0; i < extractors.Count; i++)
        {
            result[fileNames[i]] = new List<CsvRow>();
        }

        foreach (var row in rows)
        {
            for (int i = 0; i < extractors.Count; i++)
            {
                var extracted = extractors[i](row);
                if (extracted != null)
                {
                    result[fileNames[i]].Add(extracted);
                }
            }
        }

        return result;
    }
}
```

---

### Converter.cs（変換ロジック）

```csharp
public static class Converter
{
    private static readonly Dictionary<string, Dictionary<string, string>> _conversionRules = new()
    {
        { "code1", new Dictionary<string, string> { { "1", "02" }, { "2", "05" } } },
        { "multi_code", new Dictionary<string, string>
            {
                { "3", "103" },
                { "8", "108" },
                { "10", "1055" }
            }
        },
        { "code101", new Dictionary<string, string> { { "3", "555" }, { "6", "777" } } }
    };

    public static string? Convert(string ruleKey, string input)
    {
        if (ruleKey == "code101" && input == "0")
            return null;

        if (_conversionRules.ContainsKey(ruleKey) && _conversionRules[ruleKey].ContainsKey(input))
        {
            return _conversionRules[ruleKey][input];
        }

        return null;
    }

    public static List<string> ConvertAndExpandMultiple(string ruleKey, string input, string fixedValueAfterEach = "01")
    {
        var results = new List<string>();

        if (string.IsNullOrWhiteSpace(input))
        {
            return results;
        }

        string[] values = input.Split(',');

        foreach (string raw in values)
        {
            string val = raw.Trim();

            if (val == "0" || val == "")
            {
                continue;
            }

            if (_conversionRules.ContainsKey(ruleKey) && _conversionRules[ruleKey].ContainsKey(val))
            {
                string converted = _conversionRules[ruleKey][val];
                results.Add(converted);
                results.Add(fixedValueAfterEach);
            }
        }

        return results;
    }
}
```

---

### Extractor.cs（抽出ロジック）

```csharp
public static class Extractor
{
    public static CsvRow File1Extractor(CsvRow row)
    {
        var newColumns = new List<string>();

        newColumns.Add(row[0]); // 識別ID
        newColumns.Add("01");   // 固定値

        string? converted = Converter.Convert("code101", row[101]);
        if (converted != null)
        {
            newColumns.Add(converted);
        }

        var expanded = Converter.ConvertAndExpandMultiple("multi_code", row[2]);
        foreach (var item in expanded)
        {
            newColumns.Add(item);
        }

        return new CsvRow(newColumns);
    }
}
```

---

### Writer.cs

```csharp
public static class Writer
{
    public static void WriteCsv(string path, List<CsvRow> rows)
    {
        using var writer = new StreamWriter(path);
        foreach (var row in rows)
        {
            writer.WriteLine(string.Join(",", row.Columns));
        }
    }
}
```

---

### Program.cs（エントリーポイント）

```csharp
class Program
{
    static void Main(string[] args)
    {
        if (args.Length < 1)
        {
            Console.WriteLine("Usage: CsvTool <input_file>");
            return;
        }

        string inputPath = args[0];
        var rows = CsvLoader.Load(inputPath);

        var extractors = new List<Func<CsvRow, CsvRow>>
        {
            Extractor.File1Extractor,
            // Extractor.File2Extractor,
            // Extractor.File3Extractor,
            // Extractor.File4Extractor
        };

        var fileNames = new List<string>
        {
            "file1.csv",
            "file2.csv",
            "file3.csv",
            "file4.csv"
        };

        var splitted = CsvSplitter.Split(rows, extractors, fileNames);

        foreach (var kvp in splitted)
        {
            Writer.WriteCsv(kvp.Key, kvp.Value);
        }
    }
}
```

---