0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ChatGPT の表をそのまま Excel にする「Markdown → Excel 変換」を作った(SheetJS なし・ブラウザ完結)

0
Posted at

ChatGPT や Claude に「比較表を作って」と頼むと、返ってくるのは Markdown の表です。これを Excel に貼り付けると、| 項目 | 費用 | のような行がすべて A 列に入ってしまい、結局手で直すことになります。

これを一発で解決するために、Markdown の表を .xlsx に変換するツールを作って公開しました。

👉 https://md-to.com/ja/markdown-to-excel/

【スクリーンショット:変換ツールの画面】

前半で使い方、後半でブラウザ内で xlsx を生成する実装のポイントを紹介します。

できること

  • AI の回答全体や .md ファイルをそのまま貼り付けると、表だけを自動で抽出(説明文やコードブロックは無視)
  • 表が複数あれば、直前の見出しをシート名にして別々のシートに保存
  • 1,234.50 や 12.5% は数値セルとして保存されるので、そのまま SUM できる
  • ヘッダー行の太字、先頭行の固定、オートフィルター、列幅の自動調整(全角文字も考慮)
  • :---: などの列の配置は、Excel のセルの配置に反映
  • セル内の <br> は Excel のセル内改行になる
  • すべてブラウザ内で処理。データはサーバーに送信されません(無料・登録不要)

使い方

  1. ChatGPT などの回答をコピーして、左側に貼り付ける(または .md をドロップ)
  2. 右側のプレビューでシートを確認する
  3. 「.xlsx をダウンロード」を押す

Google スプレッドシートに貼りたいときは、「Excel 用にコピー」でタブ区切りのテキストをコピーすることもできます。


ここからは実装の話です。

xlsx は「XML の zip」なので自前で書ける

.xlsx の中身は、いくつかの XML を zip にまとめたものです。スタイル付きのブックを Excel で問題なく開くには、最低限次のファイルがあれば足ります。

[Content_Types].xml          各パーツの MIME タイプ
_rels/.rels                  workbook へのリレーション
xl/workbook.xml              シート一覧
xl/_rels/workbook.xml.rels   workbook から各パーツへのリレーション
xl/styles.xml                フォント、塗りつぶし、罫線、表示形式
xl/sharedStrings.xml         共有文字列テーブル
xl/worksheets/sheetN.xml     シートごとに 1 つ

XML を文字列として組み立てて、fflate で zip にするだけです。SheetJS は使っていないので、バンドルサイズもほとんど増えません。

import { strToU8, zipSync } from 'fflate';

files['xl/styles.xml'] = strToU8(styles.toXml());
files['xl/sharedStrings.xml'] = strToU8(strings.toXml());
return zipSync(files, { level: 6 });

ハマりどころ 1:要素の順序

worksheet の子要素はスキーマで順序が決まっていて、dimension → sheetViews → sheetFormatPr → cols → sheetData → autoFilter の順に並べる必要があります。順番を間違えても XML としては正しいのですが、Excel で開くと「一部の内容に問題が見つかりました。修復しますか?」と聞かれます。

先頭行の固定は sheetView の中に pane を書きます。

<sheetViews><sheetView workbookViewId="0">
  <pane ySplit="1" topLeftCell="A2" activePane="bottomLeft" state="frozen"/>
  <selection pane="bottomLeft" activeCell="A2" sqref="A2"/>
</sheetView></sheetViews>

ハマりどころ 2:シート名の制約

見出しをシート名にするときは、Excel の制約に合わせる必要があります。

  • 31 文字まで
  • [ ] : * ? / \ は使えない
  • 先頭と末尾に ' は置けない
  • 大文字小文字を区別せずに重複チェックされる
  • History は予約名

重複したときは 予算 (2) のように連番を付けています。

Markdown の表の抽出は markdown-it に任せる

最初は | で行を分割する方法を考えましたが、次のようなケースで壊れます。

  • コードブロックの中に書かれた表
  • セル内のエスケープされた \|
  • 前後の | を省略した書き方(a | b だけの行)

そこで markdown-it でパースし、table_open / th_open / inline / tr_close などのトークンを順に見て表を組み立てています。区切り行の :---: は th の style="text-align:center" として取れるので、列の配置もそのまま使えます。直前の heading_open を覚えておけば、シート名も決まります。

数値の判定:「数値にしないほうがいいもの」が大事

1,234.50 や 12.5% を数値にするのは簡単ですが、逆に数値にしてはいけないもののほうが重要でした。

const NUMBER_PATTERN = /^([-+]?)(\d{1,3}(?:,\d{3})+|\d+)(?:\.(\d+))?(%?)$/;

export function parseNumericCell(text: string) {
  const match = text.trim().match(NUMBER_PATTERN);
  if (!match) return null;

  const [, sign, intWithCommas, fraction = '', percent] = match;
  const intPart = intWithCommas.replace(/,/g, '');
  // 007 のような先頭ゼロは ID なので文字列のまま
  if (intPart.length > 1 && intPart.startsWith('0')) return null;

  // Excel は倍精度浮動小数点数なので有効数字は 15 桁まで
  const significant = (intPart + fraction).replace(/^0+/, '');
  if (significant.length > 15) return null;

  let value = Number(`${sign}${intPart}${fraction ? `.${fraction}` : ''}`);
  if (percent) value /= 100;

  // 元の見た目を表示形式で再現する(1,234.50 → #,##0.00)
  let numFmt = intWithCommas.includes(',') ? '#,##0' : '0';
  if (fraction) numFmt += `.${'0'.repeat(fraction.length)}`;
  if (percent) numFmt += '%';
  return { value, numFmt };
}
  • 先頭ゼロ:000123 のような注文番号を数値にすると 123 になってしまう
  • 15 桁の壁:16 桁以上の数値は末尾が 0 に丸められる(クレジットカード番号や長い ID が壊れる)
  • 表示形式:1,234.50 を数値にしただけだと Excel では 1234.5 と表示されるので、桁区切りと小数桁数を numFmt で再現する

列幅:全角文字は 2 文字分

列幅は各列の最長セルから計算していますが、日本語・中国語・韓国語の全角文字は Excel 上でおよそ半角 2 文字分の幅になります。全角文字の範囲を正規表現で数えて 2 倍にし、最小 8・最大 60 で丸めています。60 を超えるセルは折り返し表示にしています。

まとめ

  • xlsx は XML + zip なので、必要な部分だけなら fflate だけで自前生成できる
  • Markdown の表の抽出は、正規表現ではなく markdown-it のトークンを使うと堅い
  • 数値変換は「変換しないケース」(先頭ゼロ・15 桁超え)を先に決めておくと事故が起きにくい

AI の回答を Excel にまとめる機会が多い方は、ぜひ使ってみてください。

👉 https://md-to.com/ja/markdown-to-excel/

不具合や「こういう表がうまく変換できない」などがあれば、コメントで教えてもらえるとうれしいです。

0
1
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
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?