目次
概要
既存のExcelファイル(.xlsx)を読み込み、特定のセルに入力されている値を変更したり、新しい値を書き込んだりする実装です。
行(Row)やセル(Cell)が存在しない場合は新規作成し、存在する場合は既存のオブジェクトを取得して上書きするロジックを組み込むことで、安全に編集を行います。
仕様(入出力)
- 入力: 編集対象のファイルパス、シート番号、変更したい行・列・値
- 出力: 変更が反映されたExcelファイル(上書き保存)
- ライブラリ: NPOI (NuGetパッケージ)
実装メソッド一覧
| メソッド名 | 種類 | 説明 |
GetRow | 取得 | 指定した行番号の行オブジェクトを取得します。存在しない場合は null を返します。 |
CreateRow | 追加 | 指定した行番号に新しい行を作成します。 |
SetValue | 変更 | 指定した行・列に値を設定します。セルが存在しない場合は自動的に作成します。 |
Save | 保存 | 変更内容をファイルに書き込みます(上書き)。 |
基本の使い方
// 1. 既存ファイルを開く
var book = MyExcelBook.Open("example.xlsx");
book.SelectSheet(0);
// 2. 行を取得(なければ作成)
var row = book.GetRow(1) ?? book.CreateRow(1);
// 3. 値を変更(B2セル)
book.SetValue(row, 1, "Updated Value");
// 4. 保存
book.Save();
コード全文
using System;
using System.IO;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
class Program
{
static void Main()
{
string filePath = "example.xlsx";
// 事前にファイルがないとエラーになるため確認
if (!File.Exists(filePath))
{
Console.WriteLine("Target file not found.");
return;
}
try
{
Console.WriteLine("Opening Excel file...");
var xls = MyExcelBook.Open(filePath);
// 最初のシートを選択
xls.SelectSheet(0);
// ケース1: 既存の行(Index 1 = 2行目)の値を更新
// 行が存在しない場合に備えて「取得 ?? 作成」のパターンを使用
var row1 = xls.GetRow(1) ?? xls.CreateRow(1);
xls.SetValue(row1, 0, "Update Item"); // A列
Console.WriteLine("Row 1 updated.");
// ケース2: 離れた行(Index 5 = 6行目)に新規追加
var row5 = xls.GetRow(5) ?? xls.CreateRow(5);
xls.SetValue(row5, 2, "New Item"); // C列
Console.WriteLine("Row 5 added.");
// 保存
xls.Save();
Console.WriteLine("File saved successfully.");
}
catch (IOException ex)
{
Console.WriteLine($"File Access Error: {ex.Message}");
Console.WriteLine("Ensure the Excel file is closed.");
}
}
}
/// <summary>
/// NPOIを使用したExcel編集クラス
/// </summary>
public sealed class MyExcelBook
{
private XSSFWorkbook _workbook;
private ISheet _sheet;
private string _filePath;
private MyExcelBook()
{
_workbook = new XSSFWorkbook();
}
/// <summary>
/// 既存のExcelファイルを開きます
/// </summary>
public static MyExcelBook Open(string filePath)
{
var obj = new MyExcelBook();
obj._filePath = filePath;
// 読み込みモードで開き、メモリ上に展開する
using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
obj._workbook = new XSSFWorkbook(stream);
return obj;
}
/// <summary>
/// 操作対象のシートを選択します
/// </summary>
public void SelectSheet(int index)
{
_sheet = _workbook.GetSheetAt(index);
}
/// <summary>
/// 指定した行を取得します(存在しない場合はnull)
/// </summary>
public IRow GetRow(int rowIndex)
{
return _sheet.GetRow(rowIndex);
}
/// <summary>
/// 指定した行を新規作成します
/// </summary>
public IRow CreateRow(int rowIndex)
{
return _sheet.CreateRow(rowIndex);
}
/// <summary>
/// 指定した行・列に値を設定します(セルがない場合は作成します)
/// </summary>
public void SetValue(IRow row, int colIndex, string value)
{
// セルを取得、なければ作成
ICell cell = row.GetCell(colIndex) ?? row.CreateCell(colIndex);
// 値をセット
cell.SetCellValue(value);
}
/// <summary>
/// ファイルを上書き保存します
/// </summary>
public void Save()
{
// Createモードで開くことで上書き保存となる
using var stream = new FileStream(_filePath, FileMode.Create, FileAccess.Write);
_workbook.Write(stream);
}
}
カスタムポイント
- 数値型の対応:SetValue メソッドのオーバーロードを作成し、int や double を引数に取るメソッドを追加することで、文字列としてではなく数値としてExcelに保存できます(計算式などで利用する場合に重要です)。
- バックアップ保存:Save メソッド内で _filePath にそのまま書き込むと上書きされますが、_filePath を変更して保存すれば「別名保存」としてバックアップを残すことができます。
- セルのスタイル維持:CreateCell は新しいセルを作るため、元々の罫線や背景色が消える可能性があります。スタイルを維持したい場合は、行作成時に sourceRow.CopyRowTo を使うなど、スタイルのコピー処理を追加する必要があります。
注意点
- ファイルロック:Excelでファイルを開いたままこのプログラムを実行すると、Save メソッド(FileStream の作成時)で IOException が発生します。実行時は必ずExcelを閉じてください。
- 行の競合:CreateRow は、既にその行にデータがあっても「新しい空の行」で上書きしてしまいます。既存データを残しつつ編集したい場合は、必ず GetRow で存在確認を行い、nullの場合のみ CreateRow を呼ぶようにしてください。
- メモリ使用量:XSSFWorkbook はファイル全体をメモリに読み込みます。数メガバイト以上の巨大なExcelファイルを編集する場合はメモリ不足に注意が必要です。
応用
安全な行取得拡張メソッド
「あれば取得、なければ作成」という頻出パターンを1つのメソッドにまとめる応用です。
// クラス内に追加
public IRow GetOrCreateRow(int rowIndex)
{
return _sheet.GetRow(rowIndex) ?? _sheet.CreateRow(rowIndex);
}
// 呼び出し側がシンプルになる
var row = xls.GetOrCreateRow(1);
xls.SetValue(row, 0, "Simple update");
まとめ
Excelの値を変更する際は、「行やセルが既に存在するかどうか」を確認するステップが不可欠です。NPOIでは行やセルが存在しない場合に null が返されるため、?? 演算子などを活用して「取得できなければ作成する」というロジックを組むことで、新規書き込みと更新の両方に対応できる堅牢なコードになります。
