Mrtable: 機械可読なテキストテーブルフォーマット

概要

  • 機械可読でプレーンテキストなテーブルのフォーマット
    • mr = machine readable
    • machine readable かつ human readable(というのが欲しくて考えました)

簡単な説明

  • ベースは GFM(github flavored markdown)のテーブル
  • 各セルの内容は JSON の文字列としてパースできるように書く
    • 数や日時などもすべて文字列として扱う
    • ※ パイプ |エスケープのみ例外。後述。
| "id" | "name" | "value"      |
| ---- | ------ | ------------ |
|  "1" | "foo"  | "1\t\r\n2"   |
|  "2" | "bar"  | "1\\\"2"     |
|  "3" | "baz"  | "2019-04-13" |
|  "4" | "qux"  | "12.34"      |

  • 何もない(半角スペースのみの)セルは null
| "col1" |
| ------ |
|        |

  • パイプ | はバックスラッシュでエスケープする
    • 列の区切りと区別するため
    • しなくていい場合もありますが、よく分からなかったら「常にエスケープする」で問題ありません
| "col1" |
| ------ |
| " \| " |

基本的にはこんな感じ(最低限これだけ知ってれば書くことはできる)ですが、もうちょっといろいろあります。

もうすこし詳しい説明

  • 情報が失われない場合は両端のダブルクォートは省略可
    • 見た目の煩雑さ、手入力の際の手間を減らすため
| id  | name | value |
| --- | ---- | ----- |
|   1 | foo  | 1\n2  |

  • 情報が失われる場合は省略できない
    • 空文字
    • 半角スペースのみ
    • 文字列の左端 and/or 右端に1個以上のスペースがある
| col1 | col2  | col3  |
| ---- | ----- | ----- |
| ""   | " "   | "  "  |
| " a" | "a  " | " a " |

  • パイプ(|
    • パイプ1文字の両側に半角スペースがある場合は(ダブルクォートで囲むかどうかに関わらず)バックスラッシュエスケープ必須
| col1   | col2   |
| ------ | ------ |
| 1 \| 2 | " \| " |

実装

sonota88/mrtable

  • Ruby 用のパーサ・シリアライザ
  • エラー処理などは適当
  • v0.0.4 時点では 252 行
# v0.0.4

require 'pp'
require 'mrtable'

mrtable_text = <<'EOB'
| c1  | c2  | c3  | c4  |
| --- | --- | --- | --- |
| "123" | "abc" | "日本語" |    |
|  123  |  abc  |  日本語  |    |
| "" | " " | "  " |  |
| "\\\t\r\n\"" |  |  |  |
|  \\\t\r\n\"  |  |  |  |
| "a" | " a" | "a " | " a " |
|  a  |   a  |  a   |   a   |
| "\|" | "1 \| 2" |  |  |
|  \|  |  1 \| 2  |  |  |
| "null" | "NULL" |  |  |
|  null  |  NULL  |  |  |
EOB

header, rows = Mrtable.parse(mrtable_text)
pp header, rows

=begin

["c1", "c2", "c3", "c4"]
[["123", "abc", "日本語", nil],
 ["123", "abc", "日本語", nil],
 ["", " ", "  ", nil],
 ["\\\t\r\n" + "\"", nil, nil, nil],
 ["\\\t\r\n" + "\"", nil, nil, nil],
 ["a", " a", "a ", " a "],
 ["a", "a", "a", "a"],
 ["|", "1 | 2", nil, nil],
 ["|", "1 | 2", nil, nil],
 ["null", "NULL", nil, nil],
 ["null", "NULL", nil, nil]]

=end

puts Mrtable.generate(header, rows)

=begin

| c1         | c2     | c3     | c4    |
| ---------- | ------ | ------ | ----- |
|        123 | abc    | 日本語 |       |
|        123 | abc    | 日本語 |       |
| ""         | " "    | "  "   |       |
| \\\t\r\n\" |        |        |       |
| \\\t\r\n\" |        |        |       |
| a          | " a"   | "a "   | " a " |
| a          | a      | a      | a     |
| \|         | 1 \| 2 |        |       |
| \|         | 1 \| 2 |        |       |
| null       | NULL   |        |       |
| null       | NULL   |        |       |

=end

その他