【C#】NPOIを使ってExcelのセルに値を書き込む

目次

概要

OSSライブラリ「NPOI」を使用し、作成したExcelシートの特定のセルに値を設定する実装です。

行(Row)を作成してから、その中にセル(Cell)を確保して値を書き込むというNPOI特有の手順をラッパークラスで簡略化し、ループ処理などで効率的にデータを埋め込めるようにします。

仕様(入出力)

  • 入力: 保存先パス、シート名、書き込みたいデータ
  • 出力: データが入力された .xlsx ファイル
  • ライブラリ: NPOI (NuGetパッケージ)

実装メソッド

メソッド名説明
CreateRowシート内の指定したインデックス(0始まり)に行を作成し、その行オブジェクト(IRow)を返します。
SetCellValue渡された行オブジェクト内の指定列にセルを作成し、値を設定します。

基本の使い方

// インスタンス生成
var excel = ExcelWriter.Create("matrix.xlsx");
excel.AddSheet("ScoreData");

// 0行目を作成
var row = excel.CreateRow(0);

// (0, 0) と (0, 1) に値をセット
excel.SetCellValue(row, 0, "Alice");
excel.SetCellValue(row, 1, "95");

excel.Save();

コード全文

このコードを実行するには dotnet add package NPOI が必要です。

using System;
using System.IO;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;

class Program
{
    static void Main()
    {
        try
        {
            // 1. Excel操作クラスの初期化
            Console.WriteLine("Generating Excel file...");
            var writer = ExcelWriter.Create("GridData.xlsx");

            // 2. シートの追加
            writer.AddSheet("MultiplicationTable");

            // 3. ループ処理による値の書き込み
            // NPOIでは「行」を取得/作成してから、その行内の「セル」を操作します
            for (int r = 0; r < 9; r++)
            {
                // 行オブジェクト(IRow)を作成
                // 行番号は0始まり(0=Excelの1行目)
                IRow currentRow = writer.CreateRow(r);

                for (int c = 0; c < 9; c++)
                {
                    // 九九の計算結果を文字列として書き込み
                    var value = $"{r + 1} x {c + 1} = {(r + 1) * (c + 1)}";
                    writer.SetCellValue(currentRow, c, value);
                }
            }

            // 4. 保存
            writer.Save();
            Console.WriteLine("File saved successfully.");
        }
        catch (IOException ex)
        {
            Console.WriteLine($"File Access Error: {ex.Message}");
        }
    }
}

/// <summary>
/// NPOIのセル操作をラップするクラス
/// </summary>
public sealed class ExcelWriter
{
    private readonly XSSFWorkbook _workbook;
    private ISheet _currentSheet;
    private string _filePath;

    private ExcelWriter()
    {
        _workbook = new XSSFWorkbook();
    }

    /// <summary>
    /// 指定パスで保存するためのインスタンスを作成します
    /// </summary>
    public static ExcelWriter Create(string filePath)
    {
        return new ExcelWriter { _filePath = filePath };
    }

    /// <summary>
    /// 新しいシートを作成し、操作対象として設定します
    /// </summary>
    public void AddSheet(string sheetName)
    {
        _currentSheet = _workbook.CreateSheet(sheetName);
    }

    /// <summary>
    /// 指定した行番号に行を作成し、そのオブジェクトを返します
    /// </summary>
    /// <param name="rowIndex">行インデックス(0開始)</param>
    /// <returns>作成された行オブジェクト</returns>
    public IRow CreateRow(int rowIndex)
    {
        // シートが未作成の場合は例外またはデフォルトシート作成などの対応が必要
        if (_currentSheet == null)
        {
            throw new InvalidOperationException("Sheet must be created before adding rows.");
        }
        return _currentSheet.CreateRow(rowIndex);
    }

    /// <summary>
    /// 指定した行・列に文字列の値を設定します
    /// </summary>
    /// <param name="row">対象の行オブジェクト</param>
    /// <param name="columnIndex">列インデックス(0開始)</param>
    /// <param name="value">設定する文字列</param>
    public void SetCellValue(IRow row, int columnIndex, string value)
    {
        // セルを作成(既に存在する場合はGetCell等の判定が必要だが、新規作成前提とする)
        var cell = row.CreateCell(columnIndex);
        cell.SetCellValue(value);
    }

    /// <summary>
    /// ファイルを保存します
    /// </summary>
    public void Save()
    {
        using var fs = new FileStream(_filePath, FileMode.Create, FileAccess.Write);
        _workbook.Write(fs);
    }
}

カスタムポイント

  • 数値や日付の対応:現在の SetCellValue は文字列のみ受け取っていますが、オーバーロードを作成して doubleDateTime を受け取り、cell.SetCellValue((double)value) のように型に合わせて呼び出すと、Excel上で数値として計算可能になります。
  • 既存行の取得:CreateRow は新規作成を行いますが、既存の行に追記したい場合は _currentSheet.GetRow(rowIndex) ?? _currentSheet.CreateRow(rowIndex) のように、取得を試みてから作成するロジックに変更してください。

注意点

  1. 行オブジェクトの再利用:CreateRow は同じ行番号に対して2回呼び出すと、以前の行データを上書き(クリア)してしまいます。同じ行に複数のセルを追加する場合は、コード例のように「行オブジェクトを変数に保持」して使い回してください。
  2. インデックスの上限:Excel 2007以降(.xlsx)の仕様では最大行数は1,048,576行、最大列数は16,384列です。これを超えるインデックスを指定するとエラーにはなりませんが、Excelで開けなくなる可能性があります。
  3. パフォーマンス:NPOIはセルの数だけオブジェクトをメモリに生成します。数万行を超えるデータを扱う場合、GC(ガベージコレクション)が頻発して遅くなることがあります。

応用

数値型に対応したオーバーロードの追加

数値を文字列としてではなく、計算可能な数値データとして埋め込む拡張です。

// ExcelWriterクラスに追加
public void SetCellValue(IRow row, int columnIndex, double value)
{
    var cell = row.CreateCell(columnIndex);
    
    // 数値としてセット(Excel側でSUMなどが可能になる)
    cell.SetCellValue(value); 
}

まとめ

NPOIでのセル書き込みは「ブック → シート → 行 → セル」という階層構造を順に生成していく必要があります。特にループ処理を行う際は、行(IRow)の生成を外側のループで行い、内側のループでセル(ICell)を埋めていく構造にすることで、オブジェクトの生成コストを抑えつつ直感的なコード記述が可能になります。

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

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次