0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C++ で Excel ファイルを1つに結合する方法

0
Posted at

複数の Excel ファイルを 1 つのファイルに結合する作業は、月次レポートの集約、部門別データの統合、あるいは複数のソースから収集したデータの一元管理など、様々な業務シーンで発生します。手動で行う場合はファイルを開いてコピー&ペーストを繰り返す必要があり、作業ミスや時間的コストの原因となります。

本記事では、C++ を用いて複数の Excel ファイルをプログラム的に結合する方法を紹介します。具体的には、以下の操作を実装します:

  • 複数のワークブックを 1 つのワークブックに結合(シート単位で追加)
  • 1 つのワークブック内の複数シートを別のワークブックに結合
  • 特定のシートのみを選択して結合
  • 結合時のシート名重複への対応

環境準備

ライブラリの導入

本記事で使用するライブラリは、Excel 文書の読み書きおよび各種操作機能を提供する C++ 向けの製品です。プロジェクトへの組み込み方法は以下の通りです。

Visual Studio を使用する場合

プロジェクトを開き、「ソリューションエクスプローラー」 > 「参照」を右クリック > 「NuGet パッケージの管理」を選択します。「Browse」 タブで該当パッケージを検索し、インストールします。

手動で導入する場合

  1. 製品パッケージをダウンロードし、解凍します
  2. 解凍したフォルダ内の 「include」 と 「lib」 をプロジェクトディレクトリにコピーします
  3. プロジェクトのプロパティで、追加のインクルードディレクトリとライブラリディレクトリを設定します
  4. ソースコードに以下のインクルード文を追加します:
#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 ファイルの結合処理をプログラムで自動化することで、定期的なレポート集計作業の効率化や、複数ソースからのデータ統合を確実に行うことができます。実際の実装にあたっては、結合対象となるファイルの形式やサイズ、シート名の重複の有無を事前に確認し、適切な結合パターンを選択することが重要です。また、特に大規模なデータを扱う場合は、メモリ使用量や処理時間を考慮した設計を行うことを推奨します。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?