複数の Excel ファイルを 1 つのファイルに結合する作業は、月次レポートの集約、部門別データの統合、あるいは複数のソースから収集したデータの一元管理など、様々な業務シーンで発生します。手動で行う場合はファイルを開いてコピー&ペーストを繰り返す必要があり、作業ミスや時間的コストの原因となります。
本記事では、C++ を用いて複数の Excel ファイルをプログラム的に結合する方法を紹介します。具体的には、以下の操作を実装します:
- 複数のワークブックを 1 つのワークブックに結合(シート単位で追加)
- 1 つのワークブック内の複数シートを別のワークブックに結合
- 特定のシートのみを選択して結合
- 結合時のシート名重複への対応
環境準備
ライブラリの導入
本記事で使用するライブラリは、Excel 文書の読み書きおよび各種操作機能を提供する C++ 向けの製品です。プロジェクトへの組み込み方法は以下の通りです。
Visual Studio を使用する場合
プロジェクトを開き、「ソリューションエクスプローラー」 > 「参照」を右クリック > 「NuGet パッケージの管理」を選択します。「Browse」 タブで該当パッケージを検索し、インストールします。
手動で導入する場合
- 製品パッケージをダウンロードし、解凍します
- 解凍したフォルダ内の 「include」 と 「lib」 をプロジェクトディレクトリにコピーします
- プロジェクトのプロパティで、追加のインクルードディレクトリとライブラリディレクトリを設定します
- ソースコードに以下のインクルード文を追加します:
#include "Spire.Xls.o.h"
Excel ファイル結合の基本パターン
1. 複数のワークブックを 1 つに結合(シート単位で追加)
複数の Excel ファイルを読み込み、各ファイルのすべてのシートを新しいワークブックに追加する一般的な結合パターンです。
#include "Spire.Xls.o.h"
#include <vector>
using namespace Spire::Xls;
int main()
{
// 結合先となる新しいワークブックを作成
intrusive_ptr<Workbook> mergedWorkbook = new Workbook();
// 結合対象のファイルリスト
std::vector<std::wstring> files = {
L"売上データ_2023.xlsx",
L"売上データ_2024.xlsx",
L"売上データ_2025.xlsx"
};
// 各ファイルを順に処理
for (const auto& file : files)
{
// 対象ファイルを読み込み
intrusive_ptr<Workbook> sourceWorkbook = new Workbook();
sourceWorkbook->LoadFromFile(file.c_str());
// 対象ワークブック内の全シートを結合先にコピー
for (int i = 0; i < sourceWorkbook->GetWorksheets()->GetCount(); i++)
{
intrusive_ptr<Worksheet> sourceSheet = sourceWorkbook->GetWorksheets()->Get(i);
// 結合先に新しいシートを追加し、ソースシートの内容をコピー
intrusive_ptr<Worksheet> newSheet = mergedWorkbook->GetWorksheets()->Add();
newSheet->CopyFrom(sourceSheet);
// シート名の重複を避けるため、元のファイル名をプレフィックスとして追加
std::wstring originalName = sourceSheet->GetName();
std::wstring newName = L"[" + file + L"]" + originalName;
// シート名の長さ制限(31文字)に注意
if (newName.length() > 31)
{
newName = newName.substr(0, 31);
}
newSheet->SetName(newName);
}
sourceWorkbook->Dispose();
}
// 結合結果を保存
mergedWorkbook->SaveToFile(L"統合売上データ.xlsx", FileFormat::XLSX);
mergedWorkbook->Dispose();
return 0;
}
2. 特定シートのみを選択して結合
結合対象のファイルから、特定のシートだけを抽出して結合する方法です。
#include "Spire.Xls.o.h"
#include <vector>
#include <string>
using namespace Spire::Xls;
int main()
{
// 結合先となる新しいワークブックを作成
intrusive_ptr<Workbook> mergedWorkbook = new Workbook();
// デフォルトのシートを削除(オプション)
mergedWorkbook->GetWorksheets()->Get(0)->Remove();
// 結合対象のファイルとシート名のペア
struct FileSheetPair {
std::wstring filePath;
std::wstring sheetName;
};
std::vector<FileSheetPair> targets = {
{L"営業部報告.xlsx", L"四半期実績"},
{L"企画部資料.xlsx", L"企画_集計"},
{L"経理部データ.xlsx", L"損益計算書"}
};
for (const auto& target : targets)
{
// 対象ファイルを読み込み
intrusive_ptr<Workbook> sourceWorkbook = new Workbook();
sourceWorkbook->LoadFromFile(target.filePath.c_str());
// 指定されたシートを検索
intrusive_ptr<Worksheet> sourceSheet = sourceWorkbook->GetWorksheets()->Get(target.sheetName.c_str());
if (sourceSheet != nullptr)
{
// 結合先に新しいシートを追加してコピー
intrusive_ptr<Worksheet> newSheet = mergedWorkbook->GetWorksheets()->Add();
newSheet->CopyFrom(sourceSheet);
// シート名を設定(重複しないように調整)
std::wstring newName = target.sheetName;
int counter = 1;
while (mergedWorkbook->GetWorksheets()->Get(newName.c_str()) != nullptr)
{
newName = target.sheetName + L"_" + std::to_wstring(counter);
counter++;
}
newSheet->SetName(newName);
}
sourceWorkbook->Dispose();
}
mergedWorkbook->SaveToFile(L"選択シート結合.xlsx", FileFormat::XLSX);
mergedWorkbook->Dispose();
return 0;
}
3. 1つのワークブック内の複数シートを別のワークブックに統合
すでに複数のシートを含むワークブックから、特定のシートだけを抽出して新しいワークブックに結合する方法です。
#include "Spire.Xls.o.h"
#include <vector>
using namespace Spire::Xls;
int main()
{
// ソースワークブックを読み込み
intrusive_ptr<Workbook> sourceWorkbook = new Workbook();
sourceWorkbook->LoadFromFile(L"年間レポート.xlsx");
// 結合先の新しいワークブックを作成
intrusive_ptr<Workbook> mergedWorkbook = new Workbook();
mergedWorkbook->GetWorksheets()->Get(0)->Remove(); // デフォルトシートを削除
// 結合したいシートのインデックスリスト(例:1,3,5番目のシート)
// インデックスは0から始まる
std::vector<int> targetIndices = {0, 2, 4};
for (int idx : targetIndices)
{
if (idx < sourceWorkbook->GetWorksheets()->GetCount())
{
intrusive_ptr<Worksheet> sourceSheet = sourceWorkbook->GetWorksheets()->Get(idx);
intrusive_ptr<Worksheet> newSheet = mergedWorkbook->GetWorksheets()->Add();
newSheet->CopyFrom(sourceSheet);
newSheet->SetName(sourceSheet->GetName());
}
}
mergedWorkbook->SaveToFile(L"抽出シート結合.xlsx", FileFormat::XLSX);
sourceWorkbook->Dispose();
mergedWorkbook->Dispose();
return 0;
}
4. データ範囲を指定して結合(セル単位)
シート全体ではなく、特定のセル範囲のみを抽出して結合する方法です。各ファイルのヘッダー行を除いてデータ部分のみを結合したい場合などに有用です。
#include "Spire.Xls.o.h"
#include <vector>
using namespace Spire::Xls;
int main()
{
// 結合先ワークブックを作成
intrusive_ptr<Workbook> mergedWorkbook = new Workbook();
intrusive_ptr<Worksheet> targetSheet = mergedWorkbook->GetWorksheets()->Get(0);
targetSheet->SetName(L"統合データ");
int currentRow = 1; // 現在の書き込み行位置(1-indexed)
std::vector<std::wstring> files = {
L"支社A_データ.xlsx",
L"支社B_データ.xlsx",
L"支社C_データ.xlsx"
};
bool headerWritten = false;
for (const auto& file : files)
{
// ソースファイルを読み込み
intrusive_ptr<Workbook> sourceWorkbook = new Workbook();
sourceWorkbook->LoadFromFile(file.c_str());
intrusive_ptr<Worksheet> sourceSheet = sourceWorkbook->GetWorksheets()->Get(0);
// 使用範囲(実際にデータが存在する範囲)を取得
intrusive_ptr<CellRange> usedRange = sourceSheet->GetAllocatedRange();
if (usedRange != nullptr && usedRange->GetRowCount() > 0)
{
if (!headerWritten)
{
// 最初のファイルのヘッダー行をコピー
for (int col = 0; col < usedRange->GetColumnCount(); col++)
{
intrusive_ptr<CellRange> headerCell = usedRange->GetCell(0, col);
targetSheet->GetRange(currentRow, col + 1)->SetValue(headerCell->GetValue());
}
currentRow++;
headerWritten = true;
}
// ヘッダーを除いたデータ行をコピー
for (int row = 1; row < usedRange->GetRowCount(); row++)
{
for (int col = 0; col < usedRange->GetColumnCount(); col++)
{
intrusive_ptr<CellRange> sourceCell = usedRange->GetCell(row, col);
targetSheet->GetRange(currentRow, col + 1)->SetValue(sourceCell->GetValue());
}
currentRow++;
}
}
sourceWorkbook->Dispose();
}
mergedWorkbook->SaveToFile(L"データ統合.xlsx", FileFormat::XLSX);
mergedWorkbook->Dispose();
return 0;
}
結合時の注意点と実践的な考慮事項
シート名の重複問題
Excel では同じワークブック内に同一名のシートを複数作成することはできません。結合時にシート名が重複する場合は、自動的にリネーム処理を実装する必要があります。
// シート名の重複をチェックするユーティリティ関数の例
std::wstring GetUniqueSheetName(intrusive_ptr<Workbook> workbook, const std::wstring& baseName)
{
std::wstring uniqueName = baseName;
int counter = 1;
while (workbook->GetWorksheets()->Get(uniqueName.c_str()) != nullptr)
{
// 31文字制限を考慮
std::wstring suffix = L"_" + std::to_wstring(counter);
if (baseName.length() + suffix.length() > 31)
{
uniqueName = baseName.substr(0, 31 - suffix.length()) + suffix;
}
else
{
uniqueName = baseName + suffix;
}
counter++;
}
return uniqueName;
}
メモリ使用量への配慮
非常に多くのファイルや大規模なシートを結合する場合、メモリ使用量が増加します。必要に応じて以下の対策を検討してください:
- 不要になった
WorkbookオブジェクトはDispose()で明示的に解放する - 一度に全てのファイルを読み込むのではなく、逐次処理する
スタイルと書式の保持
CopyFrom() メソッドは、セルの値だけでなく書式やスタイルもコピーします。ただし、異なるバージョンの Excel ファイル(XLS と XLSX)間でコピーを行う場合、一部の書式が正しく反映されない可能性があります。事前にテストを行うことを推奨します。
数式の扱い
結合時に数式を含むセルをコピーする場合、参照先のセルが正しく維持されるか確認する必要があります。特に異なるワークシート間でコピーを行うと、数式内の参照が意図しないものに変わる可能性があります。結合後は数式の整合性を検証することを推奨します。
まとめ
本記事では、C++ 向けの Excel 処理ライブラリを利用して、複数の Excel ファイルを1つに結合する方法を解説しました。複数のワークブックをシート単位で結合する基本的なパターンに加え、特定シートのみを選択して結合する方法、1つのワークブック内のシートを抽出して結合する方法、さらにセル範囲を指定してデータを統合する方法についても示しました。また、シート名の重複処理やメモリ管理など、実際の実装における注意点についても触れました。Excel ファイルの結合処理をプログラムで自動化することで、定期的なレポート集計作業の効率化や、複数ソースからのデータ統合を確実に行うことができます。実際の実装にあたっては、結合対象となるファイルの形式やサイズ、シート名の重複の有無を事前に確認し、適切な結合パターンを選択することが重要です。また、特に大規模なデータを扱う場合は、メモリ使用量や処理時間を考慮した設計を行うことを推奨します。