戻る

Excel ブック差分比較

データ変換

読み込み中

ツールを読み込んでいます

ツールのコードは開いたときにのみ読み込まれます。

このツールの処理はすべてブラウザー内で行われ、入力内容はサーバーへ送信されません。

このツールについて

保存済みの 2 つの XLSX ブックを、内容を送信せず元のファイルも変更せずに比較できます。既定のブック全体モードは、完全に同じ名前のシートを対応付け、セル番地で比較します。左右から 1 シートずつ明示的に選び、1 列または複数列のキーで行を対応付けることもできます。値、型、数式の文字列、保存済みの数式結果、数値の表示形式と、ブック・シートのメタデータを別々に報告します。数値の元の格納テキストを保持し、存在しないセルと明示的な空白を区別します。差分を絞り込んでページごとに確認し、該当する全件を型付き JSON またはタグ付き CSV に出力します。ExcelJS 4.4.0 とローカル XML 検査はブラウザーワーカー内で動作し、数式・マクロ・外部データ接続は実行しません。

主な用途

  • 予算や業務レポートの新版を共有する前に、入力値、数式、古い可能性がある数式キャッシュの変更を確認する。
  • 並べ替え済みの在庫や注文行を一意の型付き ID または複合キーで照合し、文字列 001 と数値 1 を区別して両側の元の番地を保持する。
  • ブックの引き継ぎ時に、セルの変更と併せてシートの追加・削除、順序、表示状態、結合範囲、宣言範囲、列メタデータを確認する。

使い方

  1. 1.変更前と変更後の .xlsx ファイルを選び、両方を読み込みます。各ファイルは 16 MiB 以下です。シートと日付システムを確認してください。保存済みの非暗号化 XLSX コピーを使います。XLS、XLSB、XLSM の拡張子だけを変更しても変換にはなりません。
  2. 2.ブック全体の番地モードでは同名シートを自動的に対応付けます。名前を変更したシートは左右から 1 枚ずつ明示的に選びます。選択したペアでは番地モードを使うか、A,C のように最大 8 個の重複しないキー列文字を入力します。キーモードの見出し行は 1〜1048575 で、その後にあり実際のセルを持つ行だけを対象にします。
  3. 3.比較を実行し、セル変更に加えて構造イベントも確認します。キーの各要素は存在し、空白でも数式でもなく、型付きの複合キーが左右それぞれで一意である必要があります。拒否されたキーは別のコピーで修正するか、適切なキーを選び、行の対応を推測しないでください。 結果を絞り込み、1 ページ 25 イベントずつ確認します。型付き JSON のプレビューは 600 文字で省略される場合がありますが、出力には現在のページだけでなく絞り込み対象全件の完全な値を含みます。構造化データには JSON、表計算での確認には安全タグ必須の CSV を使います。消去やファイルの変更で現在のセッションを破棄します。

実行可能なブックのスナップショット比較例

数値の元のテキストを厳密に保つ

{
  "left": {
    "sheets": [
      {
        "name": "Data",
        "cells": [
          {
            "address": "A1",
            "type": "number",
            "value": "1"
          }
        ]
      }
    ]
  },
  "right": {
    "sheets": [
      {
        "name": "Data",
        "cells": [
          {
            "address": "A1",
            "type": "number",
            "value": "1.0"
          }
        ]
      }
    ]
  }
}
{
  "counts": {
    "added": 0,
    "removed": 0,
    "changed": 1
  },
  "changes": [
    {
      "kind": "cell",
      "status": "changed",
      "field": "cell",
      "leftAddress": "A1",
      "rightAddress": "A1",
      "before": {
        "type": "number",
        "value": "1",
        "formula": null,
        "cached": null,
        "numberFormat": null
      },
      "after": {
        "type": "number",
        "value": "1.0",
        "formula": null,
        "cached": null,
        "numberFormat": null
      }
    }
  ]
}

実行可能な例は小さな合成 XLSX のファイル内容を記述しており、ツールへ貼り付ける JSON ではありません。設定省略時はブック全体の番地モードです。ビルド時の検査が実際の OOXML ファイルを作成・解析し、表示されたレポート抜粋を検証します。実際の出力にはシート、キー、設定の文脈も含みます。どちらも数値の一を表しますが、XML テキストが 1 から 1.0 に変わります。丸めや許容誤差を使わず、その変更を報告します。

文字列 ID と数値を区別する

{
  "left": {
    "sheets": [
      {
        "name": "Data",
        "cells": [
          {
            "address": "A1",
            "type": "string",
            "value": "001"
          }
        ]
      }
    ]
  },
  "right": {
    "sheets": [
      {
        "name": "Data",
        "cells": [
          {
            "address": "A1",
            "type": "number",
            "value": "1"
          }
        ]
      }
    ]
  }
}
{
  "counts": {
    "added": 0,
    "removed": 0,
    "changed": 1
  },
  "changes": [
    {
      "kind": "cell",
      "status": "changed",
      "field": "cell",
      "leftAddress": "A1",
      "rightAddress": "A1",
      "before": {
        "type": "string",
        "value": "001",
        "formula": null,
        "cached": null,
        "numberFormat": null
      },
      "after": {
        "type": "number",
        "value": "1",
        "formula": null,
        "cached": null,
        "numberFormat": null
      }
    }
  ]
}

文字列 001 が数値 1 になります。セルの型と格納値の両方が変わり、先頭ゼロを黙って変換しません。

存在しないセルと明示的な空白

{
  "left": {
    "sheets": [
      {
        "name": "Data",
        "cells": [
          {
            "address": "B1",
            "type": "string",
            "value": "keep"
          }
        ]
      }
    ]
  },
  "right": {
    "sheets": [
      {
        "name": "Data",
        "cells": [
          {
            "address": "A1",
            "type": "blank",
            "value": null
          },
          {
            "address": "B1",
            "type": "string",
            "value": "keep"
          }
        ]
      }
    ]
  }
}
{
  "counts": {
    "added": 1,
    "removed": 0,
    "changed": 0
  },
  "changes": [
    {
      "kind": "cell",
      "status": "added",
      "field": "cell",
      "leftAddress": null,
      "rightAddress": "A1",
      "before": null,
      "after": {
        "type": "blank",
        "value": null,
        "formula": null,
        "cached": null,
        "numberFormat": null
      }
    }
  ]
}

B1 は両側にあるため行の有無は変わりません。変更前にない A1 が変更後に明示的な空白として存在し、セルの追加になります。

空白と空の文字列

{
  "left": {
    "sheets": [
      {
        "name": "Data",
        "cells": [
          {
            "address": "A1",
            "type": "blank",
            "value": null
          }
        ]
      }
    ]
  },
  "right": {
    "sheets": [
      {
        "name": "Data",
        "cells": [
          {
            "address": "A1",
            "type": "string",
            "value": ""
          }
        ]
      }
    ]
  }
}
{
  "counts": {
    "added": 0,
    "removed": 0,
    "changed": 1
  },
  "changes": [
    {
      "kind": "cell",
      "status": "changed",
      "field": "cell",
      "leftAddress": "A1",
      "rightAddress": "A1",
      "before": {
        "type": "blank",
        "value": null,
        "formula": null,
        "cached": null,
        "numberFormat": null
      },
      "after": {
        "type": "string",
        "value": "",
        "formula": null,
        "cached": null,
        "numberFormat": null
      }
    }
  ]
}

存在する空白 A1 が存在する空文字列になります。どちらも欠損セルではなく、型の違いを保持します。

数式が変わり、保存結果は同じ

{
  "left": {
    "sheets": [
      {
        "name": "Data",
        "cells": [
          {
            "address": "A1",
            "type": "number",
            "value": "2",
            "formula": "1+1"
          }
        ]
      }
    ]
  },
  "right": {
    "sheets": [
      {
        "name": "Data",
        "cells": [
          {
            "address": "A1",
            "type": "number",
            "value": "2",
            "formula": "2*1"
          }
        ]
      }
    ]
  }
}
{
  "counts": {
    "added": 0,
    "removed": 0,
    "changed": 1
  },
  "changes": [
    {
      "kind": "cell",
      "status": "changed",
      "field": "cell",
      "leftAddress": "A1",
      "rightAddress": "A1",
      "before": {
        "type": "number",
        "value": "2",
        "formula": "1+1",
        "cached": {
          "type": "number",
          "value": "2"
        },
        "numberFormat": null
      },
      "after": {
        "type": "number",
        "value": "2",
        "formula": "2*1",
        "cached": {
          "type": "number",
          "value": "2"
        },
        "numberFormat": null
      }
    }
  ]
}

数式が 1+1 から 2*1 に変わり、両側のキャッシュは数値 2 のままです。式を実行せず文字列を比較します。

数式は同じでキャッシュが変わる

{
  "left": {
    "sheets": [
      {
        "name": "Data",
        "cells": [
          {
            "address": "A1",
            "type": "number",
            "value": "2",
            "formula": "1+1"
          }
        ]
      }
    ]
  },
  "right": {
    "sheets": [
      {
        "name": "Data",
        "cells": [
          {
            "address": "A1",
            "type": "number",
            "value": "3",
            "formula": "1+1"
          }
        ]
      }
    ]
  }
}
{
  "counts": {
    "added": 0,
    "removed": 0,
    "changed": 1
  },
  "changes": [
    {
      "kind": "cell",
      "status": "changed",
      "field": "cell",
      "leftAddress": "A1",
      "rightAddress": "A1",
      "before": {
        "type": "number",
        "value": "2",
        "formula": "1+1",
        "cached": {
          "type": "number",
          "value": "2"
        },
        "numberFormat": null
      },
      "after": {
        "type": "number",
        "value": "3",
        "formula": "1+1",
        "cached": {
          "type": "number",
          "value": "3"
        },
        "numberFormat": null
      }
    }
  ]
}

両側とも数式は 1+1 ですが、キャッシュが 2 から 3 に変わります。意図的に不整合な合成例により、再計算ではなく保存値を報告することを確かめます。

数値の表示形式だけが変わる

{
  "left": {
    "sheets": [
      {
        "name": "Data",
        "cells": [
          {
            "address": "A1",
            "type": "number",
            "value": "1",
            "numberFormat": "0.0"
          }
        ]
      }
    ]
  },
  "right": {
    "sheets": [
      {
        "name": "Data",
        "cells": [
          {
            "address": "A1",
            "type": "number",
            "value": "1",
            "numberFormat": "0.00"
          }
        ]
      }
    ]
  }
}
{
  "counts": {
    "added": 0,
    "removed": 0,
    "changed": 1
  },
  "changes": [
    {
      "kind": "cell",
      "status": "changed",
      "field": "cell",
      "leftAddress": "A1",
      "rightAddress": "A1",
      "before": {
        "type": "number",
        "value": "1",
        "formula": null,
        "cached": null,
        "numberFormat": "0.0"
      },
      "after": {
        "type": "number",
        "value": "1",
        "formula": null,
        "cached": null,
        "numberFormat": "0.00"
      }
    }
  ]
}

格納数値は 1 のままで、表示形式が 0.0 から 0.00 に変わります。数値の変更がなくても差分になります。

番地モードでのセル移動

{
  "left": {
    "sheets": [
      {
        "name": "Data",
        "cells": [
          {
            "address": "A2",
            "type": "string",
            "value": "keep"
          },
          {
            "address": "B2",
            "type": "string",
            "value": "move"
          },
          {
            "address": "A3",
            "type": "string",
            "value": "keep"
          }
        ]
      }
    ]
  },
  "right": {
    "sheets": [
      {
        "name": "Data",
        "cells": [
          {
            "address": "A2",
            "type": "string",
            "value": "keep"
          },
          {
            "address": "A3",
            "type": "string",
            "value": "keep"
          },
          {
            "address": "B3",
            "type": "string",
            "value": "move"
          }
        ]
      }
    ]
  }
}
{
  "counts": {
    "added": 1,
    "removed": 1,
    "changed": 0
  },
  "changes": [
    {
      "kind": "cell",
      "status": "removed",
      "field": "cell",
      "leftAddress": "B2",
      "rightAddress": null,
      "before": {
        "type": "string",
        "value": "move",
        "formula": null,
        "cached": null,
        "numberFormat": null
      },
      "after": null
    },
    {
      "kind": "cell",
      "status": "added",
      "field": "cell",
      "leftAddress": null,
      "rightAddress": "B3",
      "before": null,
      "after": {
        "type": "string",
        "value": "move",
        "formula": null,
        "cached": null,
        "numberFormat": null
      }
    }
  ]
}

文字列 move が B2 から B3 に移ります。固定番地では削除と追加が 1 件ずつ生じ、行挿入や範囲移動を推測しません。

複合キーで行順を無視する

{
  "left": {
    "sheets": [
      {
        "name": "Data",
        "cells": [
          {
            "address": "A1",
            "type": "string",
            "value": "id"
          },
          {
            "address": "B1",
            "type": "string",
            "value": "quantity"
          },
          {
            "address": "C1",
            "type": "string",
            "value": "color"
          },
          {
            "address": "A2",
            "type": "string",
            "value": "001"
          },
          {
            "address": "B2",
            "type": "number",
            "value": "5"
          },
          {
            "address": "C2",
            "type": "string",
            "value": "red"
          },
          {
            "address": "A3",
            "type": "string",
            "value": "001"
          },
          {
            "address": "B3",
            "type": "number",
            "value": "8"
          },
          {
            "address": "C3",
            "type": "string",
            "value": "blue"
          }
        ]
      }
    ]
  },
  "right": {
    "sheets": [
      {
        "name": "Data",
        "cells": [
          {
            "address": "A1",
            "type": "string",
            "value": "id"
          },
          {
            "address": "B1",
            "type": "string",
            "value": "quantity"
          },
          {
            "address": "C1",
            "type": "string",
            "value": "color"
          },
          {
            "address": "A2",
            "type": "string",
            "value": "001"
          },
          {
            "address": "B2",
            "type": "number",
            "value": "8"
          },
          {
            "address": "C2",
            "type": "string",
            "value": "blue"
          },
          {
            "address": "A4",
            "type": "string",
            "value": "001"
          },
          {
            "address": "B4",
            "type": "number",
            "value": "5"
          },
          {
            "address": "C4",
            "type": "string",
            "value": "red"
          }
        ]
      }
    ]
  },
  "options": {
    "scope": "pair",
    "leftSheet": "Data",
    "rightSheet": "Data",
    "mode": "keys",
    "keyColumns": "A,C",
    "headerRow": 1
  }
}
{
  "counts": {
    "added": 0,
    "removed": 0,
    "changed": 0
  },
  "changes": []
}

A 列だけなら重複していますが、型付き A,C タプルは red と blue を区別します。2 レコードは物理的な行位置だけが変わり、値は同じです。キーモードでは行の有無のイベントを出しません。

キー照合で両側の番地を残す

{
  "left": {
    "sheets": [
      {
        "name": "Data",
        "cells": [
          {
            "address": "A1",
            "type": "string",
            "value": "id"
          },
          {
            "address": "B1",
            "type": "string",
            "value": "quantity"
          },
          {
            "address": "C1",
            "type": "string",
            "value": "color"
          },
          {
            "address": "A2",
            "type": "string",
            "value": "001"
          },
          {
            "address": "B2",
            "type": "number",
            "value": "5"
          },
          {
            "address": "C2",
            "type": "string",
            "value": "red"
          },
          {
            "address": "A3",
            "type": "string",
            "value": "001"
          },
          {
            "address": "B3",
            "type": "number",
            "value": "8"
          },
          {
            "address": "C3",
            "type": "string",
            "value": "blue"
          }
        ]
      }
    ]
  },
  "right": {
    "sheets": [
      {
        "name": "Data",
        "cells": [
          {
            "address": "A1",
            "type": "string",
            "value": "id"
          },
          {
            "address": "B1",
            "type": "string",
            "value": "quantity"
          },
          {
            "address": "C1",
            "type": "string",
            "value": "color"
          },
          {
            "address": "A2",
            "type": "string",
            "value": "001"
          },
          {
            "address": "B2",
            "type": "number",
            "value": "8"
          },
          {
            "address": "C2",
            "type": "string",
            "value": "blue"
          },
          {
            "address": "A4",
            "type": "string",
            "value": "001"
          },
          {
            "address": "B4",
            "type": "number",
            "value": "6"
          },
          {
            "address": "C4",
            "type": "string",
            "value": "red"
          }
        ]
      }
    ]
  },
  "options": {
    "scope": "pair",
    "leftSheet": "Data",
    "rightSheet": "Data",
    "mode": "keys",
    "keyColumns": "A,C",
    "headerRow": 1
  }
}
{
  "counts": {
    "added": 0,
    "removed": 0,
    "changed": 1
  },
  "changes": [
    {
      "kind": "cell",
      "status": "changed",
      "field": "cell",
      "leftAddress": "B2",
      "rightAddress": "B4",
      "before": {
        "type": "number",
        "value": "5",
        "formula": null,
        "cached": null,
        "numberFormat": null
      },
      "after": {
        "type": "number",
        "value": "6",
        "formula": null,
        "cached": null,
        "numberFormat": null
      }
    }
  ]
}

red のレコードが 2 行目から 4 行目に移り、数量が 5 から 6 に変わります。同じ複合キーで対応付け、変更セルに B2 と B4 を残します。

一意に対応できない重複キーを拒否する

{
  "left": {
    "sheets": [
      {
        "name": "Data",
        "cells": [
          {
            "address": "A2",
            "type": "string",
            "value": "001"
          },
          {
            "address": "A3",
            "type": "string",
            "value": "001"
          }
        ]
      }
    ]
  },
  "right": {
    "sheets": [
      {
        "name": "Data",
        "cells": [
          {
            "address": "A2",
            "type": "string",
            "value": "001"
          }
        ]
      }
    ]
  },
  "options": {
    "scope": "pair",
    "leftSheet": "Data",
    "rightSheet": "Data",
    "mode": "keys",
    "keyColumns": "A",
    "headerRow": 1
  }
}
{
  "error": "duplicateKeys"
}

変更前のブックには同じ文字列キー 001 が 2 件あります。最初や最後を勝手に選ばず、duplicateKeys で比較を中止します。

数式セルは行キーに使えない

{
  "left": {
    "sheets": [
      {
        "name": "Data",
        "cells": [
          {
            "address": "A2",
            "type": "number",
            "value": "1",
            "formula": "1+0"
          }
        ]
      }
    ]
  },
  "right": {
    "sheets": [
      {
        "name": "Data",
        "cells": [
          {
            "address": "A2",
            "type": "string",
            "value": "001"
          }
        ]
      }
    ]
  },
  "options": {
    "scope": "pair",
    "leftSheet": "Data",
    "rightSheet": "Data",
    "mode": "keys",
    "keyColumns": "A",
    "headerRow": 1
  }
}
{
  "error": "keyFormula"
}

A2 は数式を持ち、キャッシュ結果は数値 1 です。保存結果が ID に見えても、数式セルは適切なキーではありません。

シート名の変更を推測しない

{
  "left": {
    "sheets": [
      {
        "name": "Old",
        "cells": [
          {
            "address": "A1",
            "type": "string",
            "value": "same"
          }
        ]
      }
    ]
  },
  "right": {
    "sheets": [
      {
        "name": "New",
        "cells": [
          {
            "address": "A1",
            "type": "string",
            "value": "same"
          }
        ]
      }
    ]
  }
}
{
  "counts": {
    "added": 2,
    "removed": 2,
    "changed": 0
  },
  "changes": [
    {
      "kind": "sheet",
      "status": "removed",
      "field": "sheet",
      "before": {
        "name": "Old",
        "index": 0,
        "state": "visible",
        "dimension": null,
        "rows": [
          1
        ],
        "columns": [],
        "merges": []
      },
      "after": null
    },
    {
      "kind": "cell",
      "status": "removed",
      "field": "cell",
      "leftAddress": "A1",
      "rightAddress": null,
      "before": {
        "type": "string",
        "value": "same",
        "formula": null,
        "cached": null,
        "numberFormat": null
      },
      "after": null
    },
    {
      "kind": "sheet",
      "status": "added",
      "field": "sheet",
      "before": null,
      "after": {
        "name": "New",
        "index": 0,
        "state": "visible",
        "dimension": null,
        "rows": [
          1
        ],
        "columns": [],
        "merges": []
      }
    },
    {
      "kind": "cell",
      "status": "added",
      "field": "cell",
      "leftAddress": null,
      "rightAddress": "A1",
      "before": null,
      "after": {
        "type": "string",
        "value": "same",
        "formula": null,
        "cached": null,
        "numberFormat": null
      }
    }
  ]
}

Old と New の内容は同じでも、ブック全体モードは完全に同名のシートだけを対応付けます。シートの削除・追加と、旧 A1・新 A1 のイベントを報告します。

名前を変更したシートを明示的に選ぶ

{
  "left": {
    "sheets": [
      {
        "name": "Old",
        "cells": [
          {
            "address": "A1",
            "type": "string",
            "value": "same"
          }
        ]
      }
    ]
  },
  "right": {
    "sheets": [
      {
        "name": "New",
        "cells": [
          {
            "address": "A1",
            "type": "string",
            "value": "same"
          }
        ]
      }
    ]
  },
  "options": {
    "scope": "pair",
    "leftSheet": "Old",
    "rightSheet": "New",
    "mode": "coordinates",
    "keyColumns": "A",
    "headerRow": 1
  }
}
{
  "counts": {
    "added": 0,
    "removed": 0,
    "changed": 1
  },
  "changes": [
    {
      "kind": "structure",
      "status": "changed",
      "field": "name",
      "before": "Old",
      "after": "New"
    }
  ]
}

Old と New をペアとして選ぶと A1 は同じなので、シート名変更だけを報告します。似ているという理由で自動的に対応付けません。

シリアル 60 と日付システムを保つ

{
  "left": {
    "sheets": [
      {
        "name": "Data",
        "cells": [
          {
            "address": "A1",
            "type": "date",
            "value": "60",
            "numberFormat": "yyyy-mm-dd"
          }
        ]
      }
    ],
    "dateSystem": "1900"
  },
  "right": {
    "sheets": [
      {
        "name": "Data",
        "cells": [
          {
            "address": "A1",
            "type": "date",
            "value": "60",
            "numberFormat": "yyyy-mm-dd"
          }
        ]
      }
    ],
    "dateSystem": "1904"
  }
}
{
  "counts": {
    "added": 0,
    "removed": 0,
    "changed": 2
  },
  "changes": [
    {
      "kind": "structure",
      "status": "changed",
      "field": "dateSystem",
      "before": "1900",
      "after": "1904"
    },
    {
      "kind": "cell",
      "status": "changed",
      "field": "cell",
      "leftAddress": "A1",
      "rightAddress": "A1",
      "before": {
        "type": "date",
        "value": "60",
        "formula": null,
        "cached": null,
        "numberFormat": "yyyy-mm-dd",
        "dateSystem": "1900"
      },
      "after": {
        "type": "date",
        "value": "60",
        "formula": null,
        "cached": null,
        "numberFormat": "yyyy-mm-dd",
        "dateSystem": "1904"
      }
    }
  ]
}

シリアル文字列と表示形式は同じですが、日付システムが 1900 から 1904 になります。メタデータと日付セルの文脈が変わり、シリアルを暦日の文字列には変換しません。

構造メタデータを別に確認する

{
  "left": {
    "sheets": [
      {
        "name": "Data",
        "cells": [
          {
            "address": "A1",
            "type": "string",
            "value": "same"
          }
        ]
      }
    ]
  },
  "right": {
    "sheets": [
      {
        "name": "Data",
        "cells": [
          {
            "address": "A1",
            "type": "string",
            "value": "same"
          }
        ],
        "state": "hidden",
        "dimension": "A1:C3",
        "columns": [
          {
            "min": 1,
            "max": 2,
            "width": "12.5",
            "hidden": true
          }
        ],
        "merges": [
          "A1:B1"
        ]
      }
    ]
  }
}
{
  "counts": {
    "added": 0,
    "removed": 0,
    "changed": 4
  },
  "changes": [
    {
      "kind": "structure",
      "status": "changed",
      "field": "state",
      "before": "visible",
      "after": "hidden"
    },
    {
      "kind": "structure",
      "status": "changed",
      "field": "dimension",
      "before": null,
      "after": "A1:C3"
    },
    {
      "kind": "structure",
      "status": "changed",
      "field": "columns",
      "before": [],
      "after": [
        {
          "min": 1,
          "max": 2,
          "width": "12.5",
          "hidden": true
        }
      ]
    },
    {
      "kind": "structure",
      "status": "changed",
      "field": "merges",
      "before": [],
      "after": [
        "A1:B1"
      ]
    }
  ]
}

表示状態、宣言範囲、列幅・非表示範囲、結合範囲が変わり、A1 の値は同じです。宣言範囲や結合範囲から追加のセル値を作りません。

数式に見える文字列を報告タグで保護する

{
  "left": {
    "sheets": [
      {
        "name": "Data",
        "cells": [
          {
            "address": "A1",
            "type": "string",
            "value": "old"
          }
        ]
      }
    ]
  },
  "right": {
    "sheets": [
      {
        "name": "Data",
        "cells": [
          {
            "address": "A1",
            "type": "string",
            "value": "=SUM(1,2)"
          }
        ]
      }
    ]
  },
  "format": "csv",
  "filter": {
    "sheet": "",
    "status": "changed"
  }
}
{
  "csvRecords": [
    [
      "id",
      "kind",
      "status",
      "leftSheet",
      "rightSheet",
      "leftAddress",
      "rightAddress",
      "key",
      "field",
      "before",
      "after"
    ],
    [
      "text:1",
      "text:cell",
      "text:changed",
      "text:Data",
      "text:Data",
      "text:A1",
      "text:A1",
      "null:",
      "text:cell",
      "json:{\"type\":\"string\",\"value\":\"old\",\"formula\":null,\"cached\":null,\"numberFormat\":null}",
      "json:{\"type\":\"string\",\"value\":\"=SUM(1,2)\",\"formula\":null,\"cached\":null,\"numberFormat\":null}"
    ]
  ]
}

出力は CSV を解析したレコードです。固定ヘッダーに続く全データ項目に text:、null:、json: のタグが付きます。信頼できない =SUM(1,2) は JSON 文字列の中に残り、先頭の表計算数式にはなりません。

ブック比較でよくある間違い

  • 似たシート名や並べ替えた列が自動的に対応付けられると思い込む。
  • 一意でない業務上の名称を行キーにしたり、数式セルはキーにできないことを忘れたりする。
  • 見出し行を誤って選び、比較すべきデータをキーモードから除外する。
  • 存在しないセル、明示的な空白、空文字列を同じ状態として扱う。
  • 同じに見える数値、日付、数式キャッシュを、格納内容が同一である証拠とみなす。
  • 構造メタデータを、推定された挿入・削除・書式変更の手順として解釈する。
  • 全差分のダウンロード前に有効な絞り込みを解除し忘れる。
  • CSV の安全タグを取り除いたり、レポートをブックの更新パッチだと思ったりする。

制限と注意事項

  • 対応範囲内の非暗号化 OOXML .xlsx のみ読み込め、各ファイルは最大 16 MiB です。旧形式 XLS、バイナリ XLSB、マクロ有効 XLSM、暗号化ブック、リモート URL は非対応です。マクロ、ActiveX、埋め込みオブジェクト、外部ブックリンクを含むパッケージは拒否します。通常の外部ハイパーリンク先は無視し、取得しません。対応する通常数式と共有数式だけを読み、未対応の数式形式は失敗します。ZIP/XML の検査と資源上限により、破損・非対応・過大な入力を拒否します。ワーカーとキャンセルで画面の停止を減らしますが、ブラウザーのメモリを完全に隔離するものではありません。 1 ブックにつき最大 20 シート、100,000 セル、10,000 件の物理行記録で、実際のセルの最大行番号は 100,000、列番号は 1,024 です。列範囲記録は 2,048 件、スタイル XML ノードは 10,000 個、共有文字列は 100,000 件、テキスト値は 32,767 文字、数式は 8,192 文字までです。ZIP は最大 256 エントリー、展開後は各 16 MiB・合計 32 MiB、展開率は 200:1 までです。パッケージ全体の XML 深さは最大 48、ノードは 600,000 個です。有効な全グリッドの宣言範囲や結合範囲はメタデータとしてのみ保持する場合があり、実際のセル上限を迂回したり領域全体にセルを割り当てたりしません。 ブック全体の通常数式と展開済み共有数式は合計 4,194,304 UTF-16 コード単位までです。論理スカラー値と実効的な数値表示形式は、繰り返し参照も数えて合計 16,777,216 UTF-16 コード単位までです。ExcelJS に渡す再構築済みコア XML パッケージは、ZIP の付加容量を除き UTF-8 で最大 32 MiB です。
  • ブック全体モードでは大文字・小文字も含めてシート名を完全一致させます。明示的にペアを選ばない限り、名前変更は旧シートとそのセルの削除、新シートとそのセルの追加になります。番地モードでは同じ位置だけを比較し、移動した値は削除・追加または各番地の変更として現れます。行列の挿入、範囲移動、名前変更の推測やあいまい一致は行いません。 キーモードは選択した 1 組のシート専用です。1〜8 個の異なる Excel 列文字を指定し、左右で同じ列位置を使います。見出し行以前の行を除き、宣言だけでセルのない空行は無視します。欠損・空白・空文字列・エラー値・数式のキーセルや、重複した型付き複合キーがあると比較を中止します。文字列、数値、真偽値、日付表現を区別し、空白除去、大文字小文字変換、Unicode 正規化、数値への型変換は行いません。行座標だけの変更は無視し、変更セルには両側の番地を残します。行移動後でも宣言範囲、列、結合範囲などのシートメタデータには差分が生じる場合があります。 両ブックの符号化済み複合キーは合わせて 6 MiB までで、対応レコードが未変更でも上限を適用します。
  • セルは型付きの復号済み値で比較します。文字列は XML・共有文字列の復号後に比較し、真偽値は true/false に統一するため、同じ値の等価な符号化をバイト単位の差分とはしません。数値の XML テキストを保持し、丸めや許容誤差は使いません。Excel で同じに見えても 1、1.0、1e0 は異なる場合があります。存在しないセル、明示的な空白、空の文字列も区別します。数式の文字列と型付きのキャッシュ結果は別の項目で、キャッシュは欠損または古い場合があります。再計算も外部リンクの取得もせず、数式に見える通常の文字列は文字列のままです。 数値の日付シリアルはブックの 1900 または 1904 日付システムとともに保持し、対応する実効的な数値表示形式は別に比較し、General は特別な形式なしに統一します。日付の分類は解析値と対応する表示形式の検出に基づき、Excel の完全な描画エンジンを再現しません。明示的な ISO 日付テキストは格納されたまま比較し、ブックの日付システムだけが変わっても起点日に依存するセル変更にはしません。起点日、暦、タイムゾーンを自動変換しません。1900 システムのシリアル 60 は元の値のままとし、有効な暦日を作りません。日付システムが違う場合、同じシリアル文字列を同じ暦日と解釈しないでください。 一部の地域依存の組み込み形式は [builtin:27] のような ID マーカーで表し、既知の日付形式 ID は日付型を保持します。地域固有の表示パターンを作り出しません。
  • 構造の対象は日付システム、シートの有無・名前・順序・表示状態、宣言されたシート範囲、番地モードでの宣言された行の有無、列幅・非表示範囲、結合範囲です。物理的な空行の記録も番地モードの行イベントになり得ますが、キーモードでは行の有無のイベントを無視します。これはメタデータ差分であり編集履歴の復元ではありません。数値の表示形式は比較しますが、フォント、塗りつぶし、罫線、見た目の配置、グラフ、画像、コメント、入力規則などの未対応要素は比較しません。視覚的または完全なブック同一性の検査ではありません。リッチテキストは文字列にまとめ、結合領域や宣言範囲から追加のセルを作りません。
  • 比較は最大 20,000 イベントで、シリアライズしたイベント全体にも容量上限があります。各出力は 6 MiB まで、ワーカーの各操作は 20 秒が期限で、実際の応答はブラウザーのスケジューリングに依存します。結果や出力が上限を超えると失敗し、黙って切り詰めません。1 ページ 25 件と値プレビュー 600 文字の表示上限は出力範囲を変えません。絞り込みは表示と出力の両方に適用し、特定シートを選ぶと dateSystem などのブック全体イベントを除きます。全差分が必要ならすべての絞り込みを解除してください。 CSV は固定ヘッダーの UTF-8 差分レポートで、全データ項目にタグが必須です。text: は報告文字列や番地、null: は存在しないメタデータ、json: は変更前後の型付き JSON 値です。意図的に復号するときは先頭のタグを一度だけ外します。json:null は存在しないセル、空白セルは JSON オブジェクトです。CSV の引用符だけでは数式を防げません。タグにより信頼できない元の文字列が先頭の表計算数式にならず、後でタグを除けば解釈リスクが戻ります。JSON は構造化レポートを直接保持します。どちらも代替ブック、バイナリバックアップ、自動パッチではありません。入力や結果を送信・永続保存したり第三者 CDN に渡したりしませんが、サイト資産は通常どおり読み込み、ダウンロードしたレポートには機密情報が含まれ得ます。

よくある質問

名前を変更したシートが削除と追加になるのはなぜですか?

ブック全体モードは完全に同名のシートだけを対応付け、内容が似ているという理由では推測しません。変更前後のシートを明示的に選ぶと、セルの差分とともに名前変更を比較できます。

数式の変更は計算結果の変更を意味しますか?

必ずしもそうではありません。数式の文字列と保存済みの結果を再計算せず別々に比較します。数式が変わってもキャッシュが同じ場合や、数式が同じでもキャッシュが変化・欠損する場合があります。再計算には信頼できる表計算アプリでコピーを開いてください。

空白や重複した行キーを拒否するのはなぜですか?

一対一の対応を明確に決められないためです。キーの各要素は存在し、空白や数式ではなく、型付きタプル全体が左右それぞれで一意である必要があります。文字列 001、文字列 1、数値 1 は別のキーです。元のコピーを修正するか、適切な複合キーを選んでください。

ダウンロードは現在のページだけですか?

いいえ。出力サイズ上限の範囲内で、現在の絞り込みに一致する全イベントと完全な値を含みます。絞り込みを解除すると比較全体が対象です。長い値はプレビューで省略される場合がありますが、成功した出力では黙って省略しません。

関連ツール