Diary over Finite Fields

515ひかるの日記と雑文

Markdownのテーブル記法をCSVに変換するmdtable2csv作った

この記事の概要

  • Markdown の表を CSV に変換するコマンドラインツールを作りました
  • 表計算ソフトから Markdown の表を作成するツールはあるけど逆変換をするツールをほぼ見かけなかったので作りました
  • 日常の細かなストレスがちょっぴり軽減しました

github.com

なぜ作ったか

わたしは Markown の表を手書きすることはあまりありません。いつも Microsoft Excel などの表計算ソフトで書いて、Markdown 形式に変換をしています。変換の仕方には色々あると思いますが、わたしは普段 Markdown Tables generator という Web アプリを利用しています。

Markdown Tables generator - TablesGenerator.com

これで自分はセルの内容を書くことに集中し、パディング処理だとかに囚われず表を生成することができます。しかし、逆変換はできません。逆変換ができないと何に困るかと言うと、一度作った表を再度編集したいというときに困ります。セル内の typo を直す程度なら 直接 Markdown を編集しても良いのですが、列や行を1つ加えるなど、表の構造を変えるような編集になると面倒です。表計算ソフトを利用して表を編集したくなります。

しかし、存在するのが Markdown 記法で書かれた表だけのときに、それを表計算ソフトにサッとペーストする方法はありません(少なくともわたしは知りません)。そのため、Markdown 記法で書かれた表しか存在していないときに、簡単に表計算ソフトでその表を扱えるようにしたい。そんなモチベーションで作りました。

インストール

Go 言語の開発環境が構築されている前提ですが、下記のコマンドでインストールできます。もちろん、あらかじめ $GOPATH/bin に PATH を通してください。

$ go get -u github.com/515hikaru/mdtable2csv

バイナリのアップロードなどはしていないので、使いたければソースからビルドするしかない状態です。GitHub Actions 使って CI/CD を工夫しようかな、とは思っていますが手を動かしてはいない状態です。

使い方

わたしは標準入出力をよく扱います。特に今回のケースでは、Crowi などの Markdown ベースでの Wiki にある表を CSV に変換するのがモチベーションだったので、 Mac の pbpastexclip -o などを用いて、クリップボードの内容を標準入力から読み込んでパースできると便利かなと思いました。そのため、 cat コマンドのように何も指定しない場合は標準入力を読み込む作りにしています。

下の画像はブラウザから Markdown の表をコピーして、CSV へ変換する例です。

f:id:hikaru515:20191228175525g:plain

おまけ機能

  • Microsoft Excel 対策で BOM 付きの UTF-8 で CSV を出力する機能があります。 mdtable2csv -to-code=UTF-8-BOM とすると BOM を付けます
  • ファイルからの入力、ファイルへの出力もサポートしています。オプション名は dd コマンドに倣って mdtable2csv -if foo.md -of foo.csv という形です

実装

なぜ Golang か

リポジトリを見ればわかりますが、Go言語を使っています。理由は自分は普段 デスクトップ Ubuntu を使っていますが、仕事でもこのツールを使いたかったので他のマシンにインストールするときも簡単にできてほしいと思いました。そこでインストールが簡単な言語で、自分のマシンであればたいてい実行環境を整備してある Go にしました*1

正直インストールが簡単でさえあればいいので、 Node.js でも Rust でもよかったといえばよかったのですが、それらに比べれば Go のほうが書き慣れているかなということで Go にしました。

実装方針

Markdown パーサは gomarkdown/markdown を使用しました。CSVの出力は標準ライブラリを特に工夫せずに使っています。わたしが実装したのは Markdown の AST をたどって、表のセルを [][]string 型へと変換してやる部分だけです。2つの巨人の肩に乗り、巨人から巨人へとデータを送ることしかしていません*2

この実装方針は 10 秒くらいで目処が立ったのですが、形にするとそこそこ時間がかかるものですね。

困りゴト

特にファイルからの入力を実装したので、1回の入力で複数のテーブルが渡された時にどうしようか悩んでいます。

なんとなく、

Table 1
foo,bar
1,2
Table 2
hoge,fuga
3,4

のように、どのテーブルかわかればいいのではないかとも思うんですが、そうするとリダイレクトでCSVが作れなくなってしまいます。現在は1つめの表のみをCSVに変換するようになっています(2つ以上表が入力されても何の警告もなく無視します)。

もし自分の中で複数の表を扱うユースケースが発生すれば考えるのですが、現在は特に思いついていないのでしばらくは放置でいいかなと思っています。

おわりに

あまりきれいなコードではないですが、自分としてはこれで満足しています*3。もし機能追加の要望や、動かないなどあれば GitHub の Issues などでお知らせください。特にWindowsでの動作確認はしていないので動かないかもしれません。

そんな広く使われるツールにもならないと思うので、質問も GitHub の Issues で大丈夫です。

もし使ってみて「いいな」と思っていただけたらぜひ Star をお願いします。

*1:筆者は ghq など Go 製のコマンドラインツールをそこそこ使うので、たいていのマシンに go コマンドと GOPATH が設定されています。

*2:こんな解説、どこかで聞いたなと考えていたら VimConf 2019 の LT で登壇されていた deoplete-vim-lsp の作者の方が似たようなことを言っていました。

*3:特に機能追加にモチベーションがないのに、既存のコードベースのリファクタリングをする意味はないためです。