コンテンツにスキップ

55. 設定ファイル

CSV, INI, JSON, TOML, XML などの設定ファイルを読み書きする方法を学びます。

55.1 設定ファイルの概要

  • Siv3D は次の設定ファイルの読み書きに対応しています:
ファイル形式 読み込み 書き出し
CSV
INI
JSON
TOML
XML

55.2 CSV の読み込み

  • CSV ファイルをパースしてデータを読み込むには CSV クラスを使います
  • CSV のコンストラクタ引数に、読み込みたいテキストファイルのパスを渡します
  • ファイルパスは、実行ファイルがあるフォルダ(開発中は App フォルダ)を基準とする相対パスか、絶対パスを使用します
  • 読み込みに成功したかどうかは、if (csv), if (not csv) で調べられます
  • CSV データは Array<Array<String>> の形式で読み込まれ、添え字演算子 [row][col] によって row 行 col 列目のテキストを取得できます
  • row, col0 からカウントします
  • .rows() は CSV データの行数、.columns(row) は row 行目の列数を返します
# include <Siv3D.hpp>

struct Item
{
	// アイテムのラベル
	String label;

	// アイテムの左上座標
	Point pos;
};

void Main()
{
	// CSV ファイルからデータを読み込む
	const CSV csv{ U"example/csv/config.csv" };

	if (not csv) // もし読み込みに失敗したら
	{
		throw Error{ U"Failed to load `config.csv`" };
	}

	// 各行について
	for (size_t row = 0; row < csv.rows(); ++row)
	{
		// 各列の内容をタブ区切りで表示する
		String line;

		for (size_t col = 0; col < csv.columns(row); ++col)
		{
			line += (csv[row][col] + U'\t');
		}

		Print << line;
	}

	Print << U"----";

	// 各要素を取得し、ウィンドウやシーンの設定に反映させる
	{
		const String title	= csv[1][1];
		const int32 width	= Parse<int32>(csv[2][1]);
		const int32 height	= Parse<int32>(csv[3][1]);
		const bool sizable	= Parse<bool>(csv[4][1]);
		const ColorF background = Parse<ColorF>(csv[5][1]);

		Window::SetTitle(title);
		Window::Resize(width, height);
		Window::SetStyle(sizable ? WindowStyle::Sizable : WindowStyle::Fixed);
		Scene::SetBackground(background);
	}

	{
		const Array<int32> values = csv[6][1].split(U',').map(Parse<int32>);
		Print << values;
	}

	// アイテムの配列を CSV データから作成する
	Array<Item> items;
	{
		const size_t itemCount = Parse<size_t>(csv[7][1]);
		const size_t baseRow = 8;

		for (size_t i = 0; i < itemCount; ++i)
		{
			items << Item
			{
				.label = csv[baseRow + i * 2][1],
				.pos = Parse<Point>(csv[baseRow + i * 2 + 1][1]),
			};
		}
	}

	const Font font{ FontMethod::MSDF, 48, Typeface::Bold };

	while (System::Update())
	{
		// アイテムを描画する
		for (const auto& item : items)
		{
			const Rect rect{ item.pos, 180, 80 };
			rect.draw();
			font(item.label).drawAt(30, rect.center(), ColorF{ 0.1 });
		}
	}
}

55.3 CSV の書き出し

  • CSV ファイルを書き出すには、空の CSV オブジェクトを作成し、.writeRow(), .write(), .newLine() などを使って先頭の行からデータを追加していきます
  • フォーマット可能な値は自動的に文字列に変換されます
  • 最後に .save(path) で保存します
  • 要素が文字「,」を含む場合、その要素は「"」で囲んで保存されます

# include <Siv3D.hpp>

void Main()
{
	CSV csv;

	// 1 行書き込む場合
	csv.writeRow(U"item", U"price", U"count");
	csv.writeRow(U"Sword", 500, 1);

	// 1 項目ずつ書き込む場合
	csv.write(U"Arrow");
	csv.write(400);
	csv.write(2);
	csv.newLine();

	csv.writeRow(U"Shield", 300, 3);
	csv.writeRow(U"Carrot Seed", 20, 4);
	csv.writeRow(U"aa, bb, cc", 10, 5);
	csv.writeRow(Point{ 20, 30 }, Palette::Red, 100);

	// 保存
	csv.save(U"tutorial.csv");
	
	while (System::Update())
	{

	}
}
出力されるファイル
tutorial.csv
item,price,count
Sword,500,1
Arrow,400,2
Shield,300,3
Carrot Seed,20,4
"aa, bb, cc",10,5
"(20, 30)","(255, 0, 0, 255)",100

55.4 CSV の更新

  • 読み込んだ CSV データの一部を変更したうえで、ファイルに再保存することができます

# include <Siv3D.hpp>

void Main()
{
	CSV csv{ U"example/csv/config.csv" };

	if (not csv)
	{
		throw Error{ U"Failed to load `config.csv`" };
	}

	// データを書き換える
	csv[2][1] = Format(1280);
	csv[3][1] = Format(720);

	// データを追加する
	csv.writeRow(U"Hello.Siv3D", 12345);

	csv.save(U"tutorial.csv");

	while (System::Update())
	{

	}
}
tutorial.csv
Name,Value
Window.title,My application
Window.width,1280
Window.height,720
Window.sizable,false
Scene.background,"(0.8, 0.9, 1.0)"
Array.values,"11, 22, 33, 44, 55"
Items.count,3
Item.label,Forest
Item.pos,"(100, 100)"
Item.label,Ocean
Item.pos,"(300, 200)"
Item.label,Mountain
Item.pos,"(500, 100)"
Hello.Siv3D,12345

55.5 INI の読み込み

  • INI ファイルをパースしてデータを読み込むには INI クラスを使います
  • INI のコンストラクタ引数に、読み込みたいテキストファイルのパスを渡します
  • ファイルパスは、実行ファイルがあるフォルダ(開発中は App フォルダ)を基準とする相対パスか、絶対パスを使用します
  • 読み込みに成功したかどうかは、if (ini), if (not ini) で調べられます
  • INI データは、セクションごとに HashTable<String, String> の形式で読み込まれ、添え字演算子 [U"SECTION.NAME"] によってセクション SECTION にある名前 NAME のテキストを取得できます
# include <Siv3D.hpp>

struct Item
{
	// アイテムのラベル
	String label;

	// アイテムの左上座標
	Point pos;
};

void Main()
{
	// INI ファイルからデータを読み込む
	const INI ini{ U"example/ini/config.ini" };

	if (not ini) // もし読み込みに失敗したら
	{
		throw Error{ U"Failed to load `config.ini`" };
	}

	// 全てのセクションを列挙
	for (const auto& section : ini.sections())
	{
		// セクション名
		Print << U"[{}]"_fmt(section.section);

		// セクション内のすべてのレコードを列挙する
		for (auto&& [key, value] : section.keys)
		{
			// キーと値
			Print << U"{} = {}"_fmt(key, value);
		}
	}
	Print << U"----";

	// 各要素を取得し、ウィンドウやシーンの設定に反映させる
	{
		const String title	= ini[U"Window.title"];
		const int32 width	= Parse<int32>(ini[U"Window.width"]);
		const int32 height	= Parse<int32>(ini[U"Window.height"]);
		const bool sizable	= Parse<bool>(ini[U"Window.sizable"]);
		const ColorF background = Parse<ColorF>(ini[U"Scene.background"]);

		Window::SetTitle(title);
		Window::Resize(width, height);
		Window::SetStyle(sizable ? WindowStyle::Sizable : WindowStyle::Fixed);
		Scene::SetBackground(background);
	}

	{
		const Array<int32> values = ini[U"Array.values"].split(U',').map(Parse<int32>);
		Print << values;
	}

	// アイテムの配列を INI データから作成
	Array<Item> items;
	{
		const size_t itemCount = Parse<size_t>(ini[U"Items.count"]);

		for (size_t i = 0; i < itemCount; ++i)
		{
			items << Item
			{
				.label = ini[U"Item{}.label"_fmt(i)],
				.pos = Parse<Point>(ini[U"Item{}.pos"_fmt(i)]),
			};
		}
	}

	const Font font{ FontMethod::MSDF, 48, Typeface::Bold };

	while (System::Update())
	{
		// アイテムを描画する
		for (const auto& item : items)
		{
			const Rect rect{ item.pos, 180, 80 };
			rect.draw();
			font(item.label).drawAt(30, rect.center(), ColorF{ 0.1 });
		}
	}
}

55.6 INI の書き出し

  • INI ファイルを書き出すには、空の INI オブジェクトを作成し、.addSection(セクション名), .write(セクション, キー, 値) などを使ってセクションやレコードを追加していきます
  • 最後に .save(path) で保存します

# include <Siv3D.hpp>

void Main()
{
	INI ini;

	// セクションを追加
	ini.addSection(U"Item");
	ini.addSection(U"Setting");

	ini.write(U"Item", U"Sword", 500);
	ini.write(U"Item", U"Arrow", 400);
	ini.write(U"Item", U"Shield", 300);
	ini.write(U"Item", U"Carrot Seed", 20);
	ini.write(U"Setting", U"pos", Point{ 20, 30 });
	ini.write(U"Setting", U"color", Palette::Red);

	// 保存
	ini.save(U"tutorial.ini");
	
	while (System::Update())
	{

	}
}
tutorial.ini
[Item]
Sword = 500
Arrow = 400
Shield = 300
Carrot Seed = 20

[Setting]
pos = (20, 30)
color = (255, 0, 0, 255)

55.7 INI の更新

  • 読み込んだ INI データの一部を変更したうえで、ファイルに再保存することができます

# include <Siv3D.hpp>

void Main()
{
	INI ini{ U"example/ini/config.ini" };

	if (not ini)
	{
		throw Error{ U"Failed to load `config.ini`" };
	}

	// データを書き換える
	ini[U"Window.width"] = 1280;
	ini[U"Window.height"] = 720;

	// データを追加する
	ini.addSection(U"Siv3D");
	ini.write(U"Siv3D", U"message", U"Hello!");

	// データを削除する
	ini.removeSection(U"Item2");

	ini.save(U"tutorial.ini");

	while (System::Update())
	{

	}
}
tutorial.ini
[Window]
title = My application
width = 1280
height = 720
sizable = false

[Scene]
background = (0.8, 0.9, 1.0)

[Array]
values = 11, 22, 33, 44, 55

[Items]
count = 3

[Item0]
label = Forest
pos = (100, 100)

[Item1]
label = Ocean
pos = (300, 200)

[Siv3D]
message = Hello!

55.8 JSON の読み込み

  • JSON ファイルをパースしてデータを読み込むには JSON クラスを使います
  • 読み込みたいテキストファイルのパスを JSON::Load() に渡します
  • ファイルパスは、実行ファイルがあるフォルダ(開発中は App フォルダ)を基準とする相対パスか、絶対パスを使用します
  • 読み込みに成功したかどうかは、if (json), if (not json) で調べられます
  • JSON データは次のサンプルの ShowObject() 関数のようにして再帰的に全要素を走査できます
  • 添え字演算子 [U"NAME1"][U"NAME2]... によってパスを指定して目的の値を直接得ることもできます
# include <Siv3D.hpp>

struct Item
{
	// アイテムのラベル
	String label;

	// アイテムの左上座標
	Point pos;
};

// 再帰的に JSON の要素を表示
void ShowObject(const JSON& value)
{
	switch (value.getType())
	{
	case JSONValueType::Empty:
		Console << U"empty";
		break;
	case JSONValueType::Null:
		Console << U"null";
		break;
	case JSONValueType::Object:
		for (const auto& object : value)
		{
			Console << U"[{}]"_fmt(object.key);
			ShowObject(object.value);
		}
		break;
	case JSONValueType::Array:
		for (auto&& [index, object] : value)
		{
			ShowObject(object);
		}
		break;
	case JSONValueType::String:
		Console << value.getString();
		break;
	case JSONValueType::Number:
		Console << value.get<double>();
		break;
	case JSONValueType::Bool:
		Console << value.get<bool>();
		break;
	}
}

void Main()
{
	// JSON ファイルからデータを読み込む
	const JSON json = JSON::Load(U"example/json/config.json");

	if (not json) // もし読み込みに失敗したら
	{
		throw Error{ U"Failed to load `config.json`" };
	}

	// JSON データをすべて表示する
	ShowObject(json);

	Console << U"-----";

	// 各要素を取得し、ウィンドウやシーンの設定に反映させる
	{
		const String title	= json[U"Window"][U"title"].getString();
		const int32 width	= json[U"Window"][U"width"].get<int32>();
		const int32 height	= json[U"Window"][U"height"].get<int32>();
		const bool sizable	= json[U"Window"][U"sizable"].get<bool>();
		const ColorF background = json[U"Scene"][U"background"].get<ColorF>();

		Window::SetTitle(title);
		Window::Resize(width, height);
		Window::SetStyle(sizable ? WindowStyle::Sizable : WindowStyle::Fixed);
		Scene::SetBackground(background);
	}

	{
		Array<int32> values;
		
		for (auto&& [index, object] : json[U"Array"][U"values"])
		{
			values << object.get<int32>();
		}

		Console << values;
	}

	// アイテムの配列を JSON データから作成
	Array<Item> items;
	{
		for (auto&& [index, object] : json[U"Items"])
		{
			items << Item
			{
				.label = object[U"label"].getString(),
				.pos = Point{ object[U"pos"][U"x"].get<int32>(), object[U"pos"][U"y"].get<int32>() },
			};
		}
	}

	const Font font{ FontMethod::MSDF, 48, Typeface::Bold };

	while (System::Update())
	{
		// アイテムを描画する
		for (const auto& item : items)
		{
			const Rect rect{ item.pos, 180, 80 };
			rect.draw();
			font(item.label).drawAt(30, rect.center(), ColorF{ 0.1 });
		}
	}
}

55.9 JSON の書き出し

  • JSON ファイルを書き出すには、JSONoperator[] でデータを追加し、最後に .save(path) で保存します
  • オブジェクトは辞書順に記録されます
  • 配列の場合 json[U"Array"].push_back(100); のようにして要素を追加できます

# include <Siv3D.hpp>

void Main()
{
	JSON json;

	json[U"Item"][U"Sword"][U"price"] = 500;
	json[U"Item"][U"Arrow"][U"price"] = 400;
	json[U"Item"][U"Shield"][U"price"] = 300;
	json[U"Item"][U"Carrot Seed"][U"price"] = 20;

	json[U"Setting"][U"pos"] = Point{ 20, 30 };
	json[U"Setting"][U"color"] = Palette::Red;

	json[U"Array"].push_back(10);
	json[U"Array"].push_back(20);
	json[U"Array"].push_back(30);

	json.save(U"tutorial.json");

	while (System::Update())
	{

	}
}
tutorial.json
{
  "Array": [
    10,
    20,
    30
  ],
  "Item": {
    "Arrow": {
      "price": 400
    },
    "Carrot Seed": {
      "price": 20
    },
    "Shield": {
      "price": 300
    },
    "Sword": {
      "price": 500
    }
  },
  "Setting": {
    "color": "(255, 0, 0, 255)",
    "pos": "(20, 30)"
  }
}

55.10 JSON の更新

  • 読み込んだ JSON データの一部を変更したうえで、ファイルに再保存することができます

# include <Siv3D.hpp>

void Main()
{
	JSON json = JSON::Load(U"example/json/config.json");

	if (not json)
	{
		throw Error{ U"Failed to load `config.json`" };
	}

	// データを書き換える
	json[U"Window"][U"width"] = 1280;
	json[U"Window"][U"height"] = 720;

	// データを追加する
	json[U"Siv3D"][U"message"] = U"Hello!";

	// データを削除する
	json[U"Items"].erase(2);
	json.erase(U"Array");

	json.save(U"tutorial.json");

	while (System::Update())
	{

	}
}
tutorial.json
{
  "Items": [
    {
      "label": "Forest",
      "pos": {
        "x": 100,
        "y": 100
      }
    },
    {
      "label": "Ocean",
      "pos": {
        "x": 300,
        "y": 200
      }
    }
  ],
  "Scene": {
    "background": "(0.8, 0.9, 1.0)"
  },
  "Siv3D": {
    "message": "Hello!"
  },
  "Window": {
    "height": 720,
    "sizable": false,
    "title": "My application",
    "width": 1280
  }
}

55.11 JSON の文字列化

  • JSON.format() は JSON データを整形された String に変換します
  • .formatMinimum() は、整形のための空白や改行を省略した、最小限の String を返します

# include <Siv3D.hpp>

void Main()
{
	JSON json = JSON::Load(U"example/json/config.json");

	const String s = json.formatMinimum();

	Print << s;

	while (System::Update())
	{

	}
}
出力
{"Array":{"values":[11,22,33,44,55]},"Items":[{"label":"Forest","pos":{"x":100,"y":100}},{"label":"Ocean","pos":{"x":300,"y":200}},{"label":"Mountain","pos":{"x":500,"y":100}}],"Scene":{"background":"(0.8, 0.9, 1.0)"},"Window":{"height":600,"sizable":false,"title":"My application","width":800}}

55.12 JSON リテラル

  • 文字列リテラルに _json を付けることで JSON データを直接記述できます
  • ダブルクォーテーションを直接記述できるよう、生文字列リテラル UR を使うことを推奨します
  • 通常の String から JSON に変換するには JSON::Parse(s) を使います
  • 次のコードの MakeJSON1(), MakeJSON2(), MakeJSON3() は同じ JSON データを作成します

# include <Siv3D.hpp>

JSON MakeJSON1()
{
	JSON json;
	json[U"name"] = U"Albert";
	json[U"age"] = 42;
	json[U"object"] = JSON::Parse(U"{}");
	return json;
}

JSON MakeJSON2()
{
	return UR"({
    "name": "Albert",
    "age": 42,
    "object": {}
})"_json;
}

JSON MakeJSON3()
{
	const String s = UR"({
	"name": "Albert",
	"age": 42,
	"object": {}
})";

	return JSON::Parse(s);
}

void Main()
{
	const JSON json1 = MakeJSON1();
	const JSON json2 = MakeJSON2();
	const JSON json3 = MakeJSON3();

	Print << (json1 == json2);
	Print << (json2 == json3);

	while (System::Update())
	{

	}
}
出力
true
true

55.13 JSON のバリデーション

  • JSONValidator は、ある JSON データが適切な構造と値を持っているかを、JSON Schema に基づいて検証します
  • JSON Schema は、JSON データの構造やデータ型、値の範囲などを定義するための JSON データです
  • 文字列リテラルに jsonValidator を付けることで JSON Schema を直接記述できます
  • 次のサンプルコードでは、json1json3 が、指定された Schema に不適合であることと、その理由が表示されます

# include <Siv3D.hpp>

void Main()
{
	const JSON json1 = UR"({
    "name": "Albert",
    "age": 42,
    "object": {}
})"_json;

	const JSON json2 = UR"({
    "name": "Albert",
    "age": 42,
    "object": {
        "string": "aaaa"
    }
})"_json;

	const JSON json3 = UR"({
    "name": "Albert",
    "age": 999,
    "object": {
        "string": "bbbb"
    }
})"_json;

	const JSONValidator validator = UR"({
    "title": "A person",
    "properties": {
        "name": {
            "description": "Name",
            "type": "string"
        },
        "age": {
            "description": "Age of the person",
            "type": "number",
            "minimum": 2,
            "maximum": 200
        },
        "object": {
            "type": "object",
            "properties": {
                "string": {
                    "type": "string"
                }
            },
            "required": [
                "string"
            ]
        }
    },
    "required": [
        "name",
        "age",
        "object"
    ],
    "type": "object"
})"_jsonValidator;

	Print << U"json1:";
	{
		JSONValidator::ValidationError error;

		if (validator.validate(json1, error))
		{
			Print << U"OK";
		}
		else
		{
			Print << error;
		}
	}

	Print << U"json2:";
	{
		JSONValidator::ValidationError error;

		if (validator.validate(json2, error))
		{
			Print << U"OK";
		}
		else
		{
			Print << error;
		}
	}

	Print << U"json3:";
	{
		JSONValidator::ValidationError error;

		if (validator.validate(json3, error))
		{
			Print << U"OK";
		}
		else
		{
			Print << error;
		}
	}

	while (System::Update())
	{

	}
}
出力
json1:
[JSONValidator::ValidationError] required property 'string' not found in object
json2:
OK
json3:
[JSONValidator::ValidationError] instance exceeds maximum of 200.000000

55.14 TOML の読み込み

  • TOML ファイルをパースしてデータを読み込むには TOMLReader クラスを使います
  • TOMLReader のコンストラクタ引数に、読み込みたいテキストファイルのパスを渡します
  • ファイルパスは、実行ファイルがあるフォルダ(開発中は App フォルダ)を基準とする相対パスか、絶対パスを使用します
  • 読み込みに成功したかどうかは、if (toml), if (not toml) で調べられます
  • TOML データは次のサンプルの ShowTable() 関数のようにして再帰的に全要素を走査できます
  • 添え字演算子 [U"NAME1.NAME2.NAME3..."] によってパスを指定して目的の値を直接得ることもできます

日本語は未サポート

  • Siv3D v0.6 の TOMLReader は、非 ASCII 文字を含む TOML ファイルを読み込むことができません
  • 将来のバージョンで対応予定です
# include <Siv3D.hpp>

struct Item
{
	// アイテムのラベル
	String label;

	// アイテムの左上座標
	Point pos;
};

// 再帰的に TOML の要素を表示
void ShowTable(const TOMLValue& value)
{
	for (const auto& table : value.tableView())
	{
		switch (table.value.getType())
		{
		case TOMLValueType::Empty:
			Console << U"[Empty] " << table.name;
			break;
		case TOMLValueType::Table:
			Console << U"[Table] " << table.name;
			ShowTable(table.value);
			break;
		case TOMLValueType::Array:
			Console << U"[Array] " << table.name;
			for (const auto& element : table.value.arrayView())
			{
				switch (element.getType())
				{
				case TOMLValueType::String:
					Console << element.getString();
					break;
				case TOMLValueType::Number:
					Console << element.get<double>();
					break;
				case TOMLValueType::Bool:
					Console << element.get<bool>();
					break;
				case TOMLValueType::Date:
					Console << element.getDate();
					break;
				case TOMLValueType::DateTime:
					Console << element.getDateTime();
					break;
				default:
					break;
				}
			}
			break;
		case TOMLValueType::TableArray:
			Console << U"[TableArray] " << table.name;
			for (const auto& table2 : table.value.tableArrayView())
			{
				ShowTable(table2);
			}
			break;
		case TOMLValueType::String:
			Console << U"[String] " << table.name;
			Console << table.value.getString();
			break;
		case TOMLValueType::Number:
			Console << U"[Number] " << table.name;
			Console << table.value.get<double>();
			break;
		case TOMLValueType::Bool:
			Console << U"[Bool] " << table.name;
			Console << table.value.get<bool>();
			break;
		case TOMLValueType::Date:
			Console << U"[Date] " << table.name;
			Console << table.value.getDate();
			break;
		case TOMLValueType::DateTime:
			Console << U"[DateTime] " << table.name;
			Console << table.value.getDateTime();
			break;
		case TOMLValueType::Unknown:
			Console << U"[Unknown] " << table.name;
			break;
		}
	}
}

void Main()
{
	// TOML ファイルからデータを読み込む
	const TOMLReader toml{ U"example/toml/config.toml" };

	if (not toml) // もし読み込みに失敗したら
	{
		throw Error{ U"Failed to load `config.toml`" };
	}

	// TOML データをすべて表示する
	ShowTable(toml);

	Console << U"-----";

	// 各要素を取得し、ウィンドウやシーンの設定に反映させる
	{
		const String title	= toml[U"Window.title"].getString();
		const int32 width	= toml[U"Window.width"].get<int32>();
		const int32 height	= toml[U"Window.height"].get<int32>();
		const bool sizable	= toml[U"Window.sizable"].get<bool>();
		const ColorF background = toml[U"Scene.background"].get<ColorF>();

		Window::SetTitle(title);
		Window::Resize(width, height);
		Window::SetStyle(sizable ? WindowStyle::Sizable : WindowStyle::Fixed);
		Scene::SetBackground(background);
	}

	{
		Array<int32> values;

		for (const auto& object : toml[U"Array.values"].arrayView())
		{
			values << object.get<int32>();
		}

		Console << values;
	}

	// アイテムの配列を TOML データから作成
	Array<Item> items;
	{
		for (const auto& object : toml[U"Items"].tableArrayView())
		{
			items << Item
			{
				.label = object[U"label"].getString(),
				.pos = Point{ object[U"pos.x"].get<int32>(), object[U"pos.y"].get<int32>() },
			};
		}
	}

	const Font font{ FontMethod::MSDF, 48, Typeface::Bold };

	while (System::Update())
	{
		// アイテムを描画する
		for (const auto& item : items)
		{
			const Rect rect{ item.pos, 180, 80 };
			rect.draw();
			font(item.label).drawAt(30, rect.center(), ColorF{ 0.1 });
		}
	}
}

55.15 XML の読み込み

  • XML ファイルをパースしてデータを読み込むには XMLReader クラスを使います
  • XMLReader のコンストラクタ引数に、読み込みたいテキストファイルのパスを渡します
  • ファイルパスは、実行ファイルがあるフォルダ(開発中は App フォルダ)を基準とする相対パスか、絶対パスを使用します
  • 読み込みに成功したかどうかは、if (xml), if (not xml) で調べられます
  • XML データは次のサンプルの ShowElements() 関数のようにして再帰的に全要素を走査できます
# include <Siv3D.hpp>

struct Item
{
	// アイテムのラベル
	String label;

	// アイテムの左上座標
	Point pos;
};

// 再帰的に XML の要素を表示
void ShowElements(const XMLElement& element)
{
	for (auto e = element.firstChild(); e; e = e.nextSibling())
	{
		Console << U"<{}>"_fmt(e.name());

		if (const auto attributes = e.attributes())
		{
			Console << attributes;
		}

		if (const auto text = e.text())
		{
			Console << text;
		}

		ShowElements(e);

		Console << U"</{}>"_fmt(e.name());
	}
}

void Main()
{
	// XML ファイルからデータを読み込む
	const XMLReader xml(U"example/xml/config.xml");

	if (not xml) // もし読み込みに失敗したら
	{
		throw Error{ U"Failed to load `config.xml`" };
	}

	// XML データをすべて表示する
	ShowElements(xml);

	Console << U"-----";

	Array<Item> items;
	{
		String title;
		int32 width = Window::DefaultClientSize.x;
		int32 height = Window::DefaultClientSize.y;
		bool sizable = false;
		ColorF background{ 0.0 };
		Array<int32> values;

		// 要素を走査して目的の値を取得
		for (auto elem = xml.firstChild(); elem; elem = elem.nextSibling())
		{
			const String name = elem.name();

			if (name == U"Window")
			{
				for (auto elem2 = elem.firstChild(); elem2; elem2 = elem2.nextSibling())
				{
					const String name2 = elem2.name();

					if (name2 == U"title")
					{
						title = elem2.text();
					}
					else if (name2 == U"width")
					{
						width = Parse<int32>(elem2.text());
					}
					else if (name2 == U"height")
					{
						height = Parse<int32>(elem2.text());
					}
					else if (name2 == U"sizable")
					{
						sizable = Parse<bool>(elem2.text());
					}
				}
			}
			else if (name == U"Scene")
			{
				for (auto elem2 = elem.firstChild(); elem2; elem2 = elem2.nextSibling())
				{
					const String name2 = elem2.name();

					if (name2 == U"background")
					{
						background = Parse<ColorF>(elem2.text());
					}
				}
			}
			if (name == U"Array")
			{
				for (auto elem2 = elem.firstChild(); elem2; elem2 = elem2.nextSibling())
				{
					values << Parse<int32>(elem2.text());
				}
			}
			if (name == U"Items")
			{
				Item item;

				for (auto elem2 = elem.firstChild(); elem2; elem2 = elem2.nextSibling())
				{
					const String name2 = elem2.name();

					if (name2 == U"label")
					{
						item.label = elem2.text();
					}
					else if (name2 == U"pos")
					{
						Point pos{ 0, 0 };

						for (auto elem3 = elem2.firstChild(); elem3; elem3 = elem3.nextSibling())
						{
							const String name3 = elem3.name();

							if (name3 == U"x")
							{
								pos.x = Parse<int32>(elem3.text());
							}
							else if (name3 == U"y")
							{
								pos.y = Parse<int32>(elem3.text());
							}
						}

						item.pos = pos;
					}
				}

				items << item;
			}
		}

		Window::SetTitle(title);
		Window::Resize(width, height);
		Window::SetStyle(sizable ? WindowStyle::Sizable : WindowStyle::Fixed);
		Scene::SetBackground(background);

		Console << values;
	}

	const Font font{ FontMethod::MSDF, 48, Typeface::Bold };

	while (System::Update())
	{
		// アイテムを描画する
		for (const auto& item : items)
		{
			const Rect rect{ item.pos, 180, 80 };
			rect.draw();
			font(item.label).drawAt(30, rect.center(), ColorF{ 0.1 });
		}
	}
}