データフォーマットシリーズ、今回はTOMLです。CSV/TSV、YAMLの落とし穴 と続けてきましたが、「そういえばTOMLちゃんと書いたことないな」という方も多いのではないでしょうか。
TOMLは「設定ファイルのために作られたフォーマット」です。Rustの Cargo.toml やPythonの pyproject.toml で目にする機会が増えていますが、いざ自分で書こうとすると意外と構文がわからない。この記事では、TOMLの基本構文をゼロから整理して、設定ファイルを読み書きできるようになることを目指します。
TOMLとは
TOML(Tom’s Obvious, Minimal Language)は、GitHubの共同創設者であるTom Preston-Wernerが2013年に作ったデータフォーマットです。名前に「Tom’s」が入っているのがチャーミングですね。2021年にバージョン1.0.0がリリースされ、仕様が安定しています。
設計思想は明快で、「人間が読みやすく、コンピュータが解析しやすい、最小限の設定ファイルフォーマット」を目指しています。YAMLのように柔軟すぎず、JSONのように冗長すぎず、ちょうどいいバランスを狙ったフォーマットです。
公式サイトは toml.io で、仕様全文が公開されています。
キーと値の基本
TOMLの基本は キー = 値 のペアです。= の前後にはスペースを入れます。
# 文字列(ダブルクォートで囲む)name = "Alice"
# 整数age = 30
# 浮動小数点数height = 1.65
# 真偽値(true / false のみ。小文字限定)active = true
# コメントは # で始まるYAMLと違って、真偽値は true と false だけです。yes / no / on / off は文字列として扱われます。YAMLの落とし穴 で紹介したノルウェー問題のような暗黙変換は起きません。ここはTOMLの大きな安心ポイントです。
文字列の種類
TOMLには4種類の文字列があります。
# 基本文字列(エスケープシーケンスが使える)path = "C:\\Users\\alice"greeting = "Hello,\nWorld!"
# リテラル文字列(エスケープしない。シングルクォート)regex = '\d+\.\d+'winpath = 'C:\Users\alice'
# 複数行基本文字列description = """これは複数行の文字列です。"""
# 複数行リテラル文字列raw_text = '''エスケープなしでそのまま書ける。バックスラッシュ \ もそのまま。'''基本文字列では \n や \t などのエスケープが効きますが、リテラル文字列ではそのまま文字として扱われます。正規表現やWindowsのファイルパスを書くときにリテラル文字列が便利です。
数値
整数と浮動小数点数は明確に区別されます。
# 整数port = 8080negative = -42
# 可読性のためにアンダースコアで区切れるlarge_number = 1_000_000
# 16進数、8進数、2進数hex = 0xDEADBEEFoct = 0o755bin = 0b11010110
# 浮動小数点数pi = 3.14159scientific = 5e+22
# 特殊値infinity = infnot_a_number = nan8進数は 0o プレフィックスです。YAMLのように 0 始まりの数値が勝手に8進数に解釈されることはありません。
日時
TOMLは日時型をネイティブでサポートしています。JSONやYAMLでは文字列として扱う日時を、TOMLではファーストクラスの型として表現できます。
# オフセット付き日時(RFC 3339)created_at = 2026-03-14T10:30:00+09:00
# ローカル日時(タイムゾーンなし)updated_at = 2026-03-14T10:30:00
# ローカル日付birthday = 1990-01-15
# ローカル時刻alarm = 07:30:00日付だけ、時刻だけという書き方もできるのが便利です。
テーブル(セクション)
テーブルはキーと値をグループ化する仕組みで、TOMLの中核的な構文です。[テーブル名] のヘッダーで定義します。
[server]host = "localhost"port = 8080
[database]host = "db.example.com"port = 5432name = "myapp"これはJSONに変換すると以下のようになります。
{ "server": { "host": "localhost", "port": 8080 }, "database": { "host": "db.example.com", "port": 5432, "name": "myapp" }}テーブルはネストもできます。ドットで区切るだけです。
[server.production]host = "prod.example.com"port = 443
[server.development]host = "localhost"port = 3000ドットキー
テーブルヘッダーを使わなくても、ドット区切りのキーで階層構造を表現できます。
# この2つは同じ意味fruit.apple.color = "red"
# [fruit]# [fruit.apple]# color = "red"フラットに書きたい場合はドットキー、まとまった設定を書く場合はテーブルヘッダー、と使い分けるのがおすすめです。
インラインテーブル
短いテーブルは1行で書くこともできます。
# インラインテーブルpoint = { x = 1, y = 2 }
# 通常のテーブルで書くとこう[point]x = 1y = 2ただし、インラインテーブルは改行できません。要素が多い場合は通常のテーブルを使いましょう。
配列
配列は角括弧 [] で定義します。
# 基本的な配列colors = ["red", "green", "blue"]
# 複数行で書くこともできるports = [ 8080, 8443, 9090,]
# 末尾カンマOK(ここ嬉しい)tags = [ "web", "api", "backend",]末尾カンマが許されているのは地味にありがたいポイントです。要素の追加・削除のときにdiffがきれいになります。
配列テーブル(Array of Tables)
テーブルの配列を定義するには [[テーブル名]] を使います。ダブルブラケットです。
[[products]]name = "Hammer"price = 9.99
[[products]]name = "Nail"price = 0.05sku = 284758393これはJSONでは以下のように表現されます。
{ "products": [ { "name": "Hammer", "price": 9.99 }, { "name": "Nail", "price": 0.05, "sku": 284758393 } ]}[[products]] を書くたびに配列に新しい要素が追加されていくイメージです。設定ファイルで「複数のサーバー定義」や「複数の依存関係」を表現するのに使われます。
実際の利用例
TOMLがどんな場面で使われているか、代表的な例を見てみましょう。
Cargo.toml(Rust)
Rustのパッケージマネージャ Cargo の設定ファイルです。TOMLの採用例としてもっとも有名でしょう。
[package]name = "my-app"version = "0.1.0"edition = "2021"authors = ["Alice <alice@example.com>"]description = "A sample Rust application"
[dependencies]serde = { version = "1.0", features = ["derive"] }tokio = { version = "1", features = ["full"] }
[dev-dependencies]assert_cmd = "2.0"
[[bin]]name = "my-app"path = "src/main.rs"[dependencies] セクションにパッケージ名と要件を書くシンプルな構成です。serde = { version = "1.0", features = ["derive"] } のようにインラインテーブルを使って、バージョンに加えてfeatureフラグを指定できます。[[bin]] は配列テーブルで、複数のバイナリターゲットを定義できます。
pyproject.toml(Python)
PythonではPEP 518以降、pyproject.toml がプロジェクト設定の標準になりつつあります。
[project]name = "my-package"version = "1.0.0"description = "A sample Python package"requires-python = ">=3.9"dependencies = [ "requests>=2.28", "click>=8.0",]
[project.scripts]my-cli = "my_package.cli:main"
[tool.ruff]line-length = 88target-version = "py39"
[tool.ruff.lint]select = ["E", "F", "I"]
[tool.pytest.ini_options]testpaths = ["tests"][tool.xxx] のようにツール固有の設定を名前空間で分けられるのが便利です。以前は setup.py、setup.cfg、requirements.txt、各ツールの設定ファイルとバラバラだったものが、1つのファイルにまとまります。
その他の利用例
TOMLは他にもさまざまなツールで採用されています。
- Hugo(静的サイトジェネレーター):
hugo.tomlでサイト設定 - Taplo(TOMLフォーマッター):
taplo.tomlで設定 - Deno:
deno.tomlでプロジェクト設定(JSON も可) - Starship(シェルプロンプト):
starship.tomlで見た目のカスタマイズ
JSON・YAMLとの比較
設定ファイルとしてよく使われる3フォーマットを簡単に比較してみます。
| 項目 | TOML | JSON | YAML |
|---|---|---|---|
| 用途 | 設定ファイル | データ交換 | 設定ファイル / データ |
| コメント | # で可能 | 不可 | # で可能 |
| 型の明確さ | 高い(暗黙変換なし) | 高い | 低い(暗黙変換あり) |
| 真偽値 | true / false のみ | true / false のみ | yes / no / on / off なども(v1.1) |
| 日時型 | ネイティブサポート | なし(文字列で表現) | なし(文字列で表現) |
| ネスト表現 | テーブルヘッダー / ドットキー | 波括弧のネスト | インデント |
| 末尾カンマ | 配列で可能 | 不可(仕様上) | N/A |
| 仕様の大きさ | 小さい | 小さい | 大きい |
TOMLは設定ファイルに特化しているぶん、仕様がコンパクトで覚えやすいです。一方で、深いネスト構造やツリー状のデータはTOMLだと冗長になりがち。用途に応じた使い分けが大事です。
ざっくりまとめると——
- TOML: 設定ファイル。フラットから中程度のネストに向いている
- JSON: API間のデータ交換、構造化データの保存
- YAML: 深いネスト構造のある設定、CI/CD、Kubernetes
よくあるつまずきポイント
TOMLはYAMLに比べると罠が少ないですが、いくつか注意点があります。
テーブルの定義順
同じテーブルのキーは散らばらせることができません。
# NG: [server] を2回定義できない[server]host = "localhost"
[database]name = "myapp"
[server] # エラー! すでに定義済みport = 8080テーブルに属するキーはまとめて書く必要があります。
インラインテーブルの制約
インラインテーブルは後から要素を追加できません。
# インラインテーブルで定義した後に追加はできないpoint = { x = 1, y = 2 }# point.z = 3 # エラー!要素の追加が必要な場合は、通常のテーブル記法を使いましょう。
深いネストの冗長さ
TOMLは深いネストが苦手です。3〜4階層くらいまでなら問題ありませんが、それ以上になるとテーブルヘッダーが長くなって読みづらくなります。
# テーブルヘッダーが長くなりがち[services.web.production.logging]level = "info"format = "json"
[services.web.production.database]host = "db.example.com"深いネスト構造が多いなら、YAMLのほうが見やすい場合もあります。適材適所で使い分けましょう。
まとめ
TOMLの基本構文を一通り見てきました。ポイントを振り返ります。
- TOMLは設定ファイルのために設計されたフォーマット。仕様が小さく、覚えやすい
- 基本はキーと値のペア。文字列、整数、浮動小数点数、真偽値、日時をサポート
- テーブル(
[section])でグループ化、配列テーブル([[array]])で配列を表現 - 暗黙の型変換がないのでYAMLのような罠が少ない。真偽値は
true/falseのみ - Cargo.toml、pyproject.toml など、モダンなツールで広く採用されている
- 深いネストには向かない。フラット〜中程度のネスト構造が得意
「設定ファイルを書くなら、まずTOMLを検討する」——これが今のトレンドです。仕様がシンプルなぶん、この記事の内容を押さえればほとんどのTOMLファイルを読み書きできるようになるはずです。
TOMLって名前からして「トムさんのわかりやすい言語」なんだよね。実際、YAMLみたいな暗黙変換の罠がないから安心して書けるんだ。Cargo.toml とか pyproject.toml を見かけたら、もう怖がらなくて大丈夫!あたし的には末尾カンマOKなところが地味に推しポイント。さあ、手元のプロジェクトで試してみよう!