テキストベースの平易なデータ形式３選

バイナリは嫌だ、テキストベースがよいという話はよく聞きます。
それにしても色々ありますね。CSVとかJSONとか。どうやって選べばいいのでしょうか。

# 簡単ルール

データ構造によって選ぶべきだと思うのです。

* 「二次元配列」または「固定的なキーバリューの任意個の羅列」として表現できれば CSV
* 「キーバリューの任意個の羅列」として表現できれば LTSV
* その他のデータ構造ならば JSON

#　CSV

CSV (Comma-Separated Values) はざっくりこんな感じです。

* CSV は任意個の行の連なったテキスト
* 行は複数フィールドをコンマで連結したもの（各行のフィールド数は同一である**べき**）
* 先頭行はフィールド名を与えるということにしても**よい**

```text:csv
field_name,field_name,field_name
aaa,bbb,ccc
zzz,yyy,xxx
```

蛇足：この例は IETF RFC 4180 https://datatracker.ietf.org/doc/html/rfc4180 に載っているものです。フィールド値にコンマ、ダブルクォート、改行が入ると面倒なことになりますが、興味があれば RFC 4180 みてください。

## 二次元配列的な使い方

二次元配列としての使い方はたとえばこんなもんですね。べつに縦横軸は経緯度でなくてもいいし、データは気温じゃなくてもいいし、数値じゃなくて順位や名称でもかまわないですが、同種のデータがなんらかの「羅列の羅列」になっている場合はわかりやすく使えます。

||135.0°E|135.5°E|136.0°E|
|--:|:--:|:--:|:--|
|35.0°N|6.0℃|3.8℃|7.3℃|
|34.5°N|3.2℃|6.1℃|6.4℃|
|34.0°N|6.1℃|3.1℃|3.6℃|

これを CSV にしたらこんなもんですね。

```text:meshdata.csv
6.0,3,8,7.3
3.2,6.1,6.4
6.1,3.1,3.6
```

ただ、ファイルにした例を見ればわかりますが、「縦横軸が緯度経度であり始点・間隔はいくら」「データは●年●月●時の気温であり単位は℃」のような情報（メタデータ）を書き込んでいく場所はありません。無理にやるとあんまり素敵なことにはなりません。

そこまで丁寧にしたいならメタデータを別ファイルにするか、気象データであれば GRIB か netCDF を使うべきでしょう。

https://qiita.com/e_toyoda/items/ce7497e1a633b16f1ff1

https://qiita.com/e_toyoda/items/374e32f9de4e3acefb9c

## 固定的なキーバリューの任意個の羅列としての使い方

CSV は列ごとに違う種類のデータを書くような使い方もできます。RFC4180 で一行目は列の名前で会ってよい、各行で列の数が同じであるべきといっていることからわかるように、行と列の役割を入れ替えることはできません。固定的な役割をもった列をあらかじめ決めておいて、行の数はデータ増加につれて増えていくという使い方が求められます。

関係データベース（MariaDB や PostgreSQL など）がこういったデータ構造ですね。データ構造の名前としては、コッドの正規形というと数学的にいかめしいわりに使い方がよくわからないので、ちょっとだけ実用的な「整然データ（tidy data）」 という概念・用語がよいと考えます。

https://id.fnshr.info/2017/01/09/tidy-data-intro/

このようなデータ構造の例としては、上のサイトにもありますが、地点の気象観測が典型的でしょう。下例は同時刻の複数地点ですが、ある地点の時系列でも、複数地点の時系列の集合でも、書式を変えずに対応できます。

|地点|緯度/度|経度/度|日時|気温/K|露点温度/K|海面更正気圧/Pa|風向/ 度|風速/ m.s-1|
|--:|:--|:--|:--|:--|:--|:--|:--|:--|:--|
|東京|35.68|139.75|2022-02-22T00:00Z|277.55|263.45|101690|330|2.6|
|大阪|34.66|135.51|2022-02-22T00:00Z|277.75|270.45|102350|250|4.6|
|福岡|33.58|130.37|2022-02-22T00:00Z|279.75|265.75|103000|290|5.7|

これをCSVにすると、様式はあくまでたとえばですが、こんな感じにできるでしょう。

```CSV:sfcobs.csv
station,lat,lon,time,temp,dewpt,pres,wdir,wspd
0-20000-0-47662,35.68,139.75,2022-02-22T00:00Z,277.55,263.45,101690,330,2.6
0-20000-0-47772,34.66,135.51,2022-02-22T00:00Z,277.75,270.45,102350,250,4.6
0-20000-0-47807,33.58,130.37,2022-02-22T00:00Z,279.75,265.75,103000,290,5.7
```

まあほとんど縦罫線をコンマに書き換えただけですが、データとして使いやすいように地点名を WMO の決めている地点番号（WIGOS Station Identifier）に置き換えています。列名は思いつきです。

https://community.wmo.int/wigos-station-identifier

# LTSV

整然データ、大いに結構ですが、実際にシステムを運営していると、つらいこともあります。

* 列の数や意味を後から変えたいことがある
* 行によってものすごく構造が違うデータをひとまとめにしたいことがある

そんなあなたのために LTSV (Labeled Tab-Separated Values) というものがあります。
各列にラベルをつけることで、列の役割を自由にできるのです。

https://qiita.com/hit/items/3c4276db1d4427fcee7a

上記の例を表現すれば下のような感じでしょうか。本当はタブ区切りですが入力できないのでスペースにしています。そして、整然データな例なので LTSV のありがたみがありませんが、たとえば p が一部にしかないとか、後から新要素が追加となっても対応できると思って見てください。

```sfcobs.ltsv
wsi:0-20000-0-47662 lat:35.68 lon:139.75 time:2022-02-22T00:00Z t:277.55 td:263.45 p:101690 d:330 f:2.6
wsi:0-20000-0-47772 lat:34.66 lon:135.51 time:2022-02-22T00:00Z t:277.75 td:270.45 p:102350 d:250 f:4.6
wsi:0-20000-0-47807 lat:33.58 lon:130.37 time:2022-02-22T00:00Z t:279.75 td:265.75 p:103000 d:290 f:5.7
```

このように、任意の名前と値の組が任意個あるようなデータ構造をキーバリューとかいうようです。

# JSON は最後の一手じゃないかな

最後に JSON について。いま大変人気があります。配列とキーバリューの組み合わせのどんな構造でも表現できるからですが、困ったこともあります。

* 拡張性のダークサイド：JSON を読むプログラムは、どんな構造がやってくるかわからないのに備えねばなりません。
* CSV や LTSV は、ファイルを連結しても CSV や LTSV として読めますが、 JSON はそうではありません。
* ありきたりのパーザは、巨大な JSON ファイルの一部だけを読みだすということができず、一度メモリを使って JSON ファイルの構造を全部解読してから、利用者がその一部を取り出さなければなりません。

なので、JSON 一択でいくと人生はあまりよいものではなくて、 CSV や LTSV で表現できないとわかっている時だけ使うといった工夫が望まれると思うのでした。