これは何?
ソフトウェアを新たに起こしていて。
設定を変えられるようにする必要があり。
設定の GUI とか作るのだるいので設定ファイルをユーザーに書いてもらおうと思い。
設定ファイルの書式として何を選ぶのがいいのかなと思って調べたので書いてみた。
登場人物
候補に上がったものと、そういう設定ファイルの形式あるよねと思ったけどまったく採用する気がなかったものを列挙すると。
名前と Wikipedia 等へのリンク | ひとこと |
---|---|
INI | 無理 |
XML | 人類向きじゃない |
YAML | 複雑 |
JSON | コメント書けない。ケツカンマ問題がある |
JSONC | 有力候補 |
JSON5 | 有力候補 |
TOML | 1.0.0 がリリースされたばかり |
ケツカンマ問題とは
たとえば JSON で
[1,2,3,4]
なら末尾のカンマが無いのが自然だし、末尾にカンマを書きたいモチベーションもほとんどない。しかし
[
{ "name": "An object that takes many lines to describe" },
{ "name": "Many lines of definition" },
{ "name": "Multi-line object" }
]
のような場合、末尾の要素を末尾以外に移動したり、末尾に要素を追加したりすると「ここにカンマがあると文法エラーです」とか「ここにはカンマが必要です」とかいう文法エラーに見舞われる。というわけで、順序変更や末尾付近で追加削除するときにストレスを感じる。
これがケツカンマ問題である。
ケツカンマ問題が問題なのは、行末にコンマが必要な場所と行末のコンマが禁止の場所が両方あるからなので
- 全部コンマありなら問題ないような文法
- 全部コンマなしなら問題ないような文法
のいずれかなら人間としては楽になる。
具体的には、
- 最後の要素の後にコンマがあっても許すので、全部コンマありにすれば良い(JSON5 など)
- 行末にコンマ不要なので、全部コンマなしにすれば良い(TOML など)
のような対応がある。
ひとつひとつ説明
INI
なんとなく人間が書くのに便利に見える感じだけど、仕様が不明瞭だし、文字コードもどうすればいいのかよくわからないのでお勧めできない。
歴史的経緯以外に使うべき理由はまったくないと思う。
少なくとも Windows 3.1 の頃から使われている。
Win32 API で読み書きできる。
Win32 API で書いた INI ファイルを Win32 API で読むなら問題ないんだけど、手で書いた INI ファイルを Win32 API で読むとなると問題があり。
私の記憶が確かなら、 Win9x 系 と WinNT 系で、同じファイルを読んだ結果が異なる場合があったと思う。等号の前後に空白がある場合の動作だったかなぁ。よくおぼえてない。
キー・バリューペアは「セクション」と呼ばれるブロックでグループ化されるんだけど、セクションのネストができないので割と困る。
Python に configparser
というライブラリがあってINI形式にということになっているけど、Windows の INI ファイルと似ているだけで違う形式。ややこしい。
configparser
の INI も、軽く探した範囲内では文法の仕様が見つけられなかった。
XML
人間が書くのはつらそうな見た目だけど、仕様も明瞭で文字コードもどうすればいいのか明らかという INI とは対照的な感じ。
書きやすさということでは、閉じタグを書くのがだるすぎる。
値に型はなく、 1
が数なのか文字列なのかはアプリケーションが決める。
それと。
<foo hoge="1"></foo>
と
<foo><hoge>1</hoge></foo>
のどっちにするのがいいのかわからないという問題があり、めんどくさい。
さらに。
<foo>
<hoge>
1
</hoge>
</foo>
のようにしたときに「1
」の前後やタグの前後にある空白改行をどうするかという問題もある。これは XML の仕様ではなく、読み取るアプリケーションの仕様で決めることになる。
各 XML がアプリケーションにとって妥当かどうかを確認するための仕組みがあるのは便利だと思う。その仕組のための XML を書くのが面倒だけど。
YAML
私自身はほぼ使ったことがないんだけど
- 人間が書くためにデザインされている
- 値に型がある
- 設定ファイルを書くために必要な機能はだいたい揃っている
ということで、大いに期待していた。
文字コードについての明確なルールがあるのかどうかはよく知らないが、utf-8 で書くのが一般的という暗黙の了解がありそう。
しかし。
文法がわりとリッチで、そんなに機能はいらないよ、という気分になる。
それと。
文法にバージョンがあるのが地味に罠になるらしい。
ノルウェー問題
YAML.load( "{Japan: {lang: ja}, Norway: {lang: no}}" )
# => {"Japan"=>{"lang"=>"ja"}, "Norway"=>{"lang"=>false}}
と呼ばれる問題の存在に気づいてからは、だいぶ使いたい気持ちがなくなった。
JSON
よく使う。色々なツール・ライブラリが対応していて安心感がある。
文法も簡単。必須と思われる機能はほぼ揃っている。
この記事を書くときまで知らなかったんだけど、文字コードも BOMなしUTF-8 必須と決まっているらしい。
多くのパーサが文法エラーの JSON を読んだときに「エラーです」と言ってくれるので安心感があるんだけど、浮動小数点数の非数などを JSON にすると不正なJSONをつくるライブラリがわりとたくさんあるので要注意 (詳しくは、拙記事非数をJSONに入れようとするとどうなるか を参照)。
RFC もある。素晴らしい。
しかし。
- コメントを書く方法がない。
- ケツカンマ問題がつらい。
- 16進数が書けない。
- 浮動小数点数の非数と無限を入れる方法がない。
という欠点がある。特にコメントが書けないのが痛い。
JSONC
よく使う。
JSON の「コメントが書けない」という欠点を補ったもの。
Visual Studio Code の設定ファイルで使われている。
便利なのでわりと使うけど、仕様はいまいちはっきりしない。
文字コードの規定もはっきりしない。
ケツカンマ問題は改善されていないし、16進数も書けない。
JSON5
JSON の欠点を解消したもの。
仕様も明瞭。すばらしい。
コメントも書けるし、ケツカンマ問題も解消されている。
浮動小数点数の非数の無限も入れられる。
16進数も複数行に渡る文字列も書ける。
それでいて必要を超えないシンプルな仕様。
文字コードに関する規定がないように思うのがほんの少し残念だけど、まあいいとおもう。
しかしなぜかあまり広まっている気がしない。
TOML
期待のホープ。
仕様は明瞭。日本語訳もある。
コメントも書ける。
値に型もある。日付型をサポートしているのが地味に便利。
浮動小数点数の非数も無限も入れられる。
ケツカンマ問題も完全に解消されている。
16進数・8進数・2進数も書ける。
文字コードの規定も明瞭。
歴史が浅くあまり知られていないと思うけど、これが広まってほしいなぁ。
とはいえ欠点もある。
JSON なら
{"a":[1,[2,{"b":1}]]}
となるようなものを TOML で書くのが難しい。
ruby の TomlRB で dump しようとするとうまくできないぐらいには難しい。
書けるとしても、どうやって書けばいいのか悩ませてしまうのはよくない。
設定ファイルということだとこういう複雑さにはなりにくいかもしれないけど、困ることはあるだろう。
Jesth
まだちゃんと読んでないのでリンクだけ。
まとめ
私見を中心にまとめた。
「書きやすい」とか「有名?」は完全に私見。
「RFCがある」の欄は、無いことを確認するのは難しいのであまり自信がない。
下表の I, X, Y, J, JC, J5 T は、順に INI、XML、YAML、JSON、JSONC、JSON5、TOML。
特徴 | I | X | Y | J | JC | J5 | T |
---|---|---|---|---|---|---|---|
人間が書きやすい | 良 | 悪 | 良 | 普通 | やや良 | 良 | 良 |
コメント書ける | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ |
階層化 | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
値に型がある | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ |
非数と無限 | n/a | n/a | ✅ | ❌ | ❌ | ✅ | ✅ |
16進数 | n/a | n/a | ✅ | ❌ | ❌ | ✅ | ✅ |
文字コードをどうすればいいか明瞭 | ❌ | ✅ | ❓ | ✅ | ❓ | ❓ | ✅ |
仕様が明瞭 | ❌ | ✅ | ✅ | ✅ | ❓ | ✅ | ✅ |
RFCがある | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
仕様が簡単? 複雑? | n/a | 複雑 | 複雑 | 簡単 | n/a | 普通 | 普通 |
有名? | 普通 | 有名 | やや有名 | 有名 | やや有名 | 普通 | 無名 |
といわけで、 TOML がんばれ。