読み書きそろばん!
読んで書いたらそろばん!
次は、読み込んだデータを自分の好きなように加工する方法を考えよう。
まずは、全部の行データを保存するためにvectorを導入しよう。
通常の配列だと、読み込む行数がわからないときにはとても苦労する(昔はファイルサイズから行数を計算したり、一回読み込んで行数だけ数えてクローズしてからもう一回開いたり、いろいろやったなぁ)。vectorなら、追加自由なので読んだらプッシュバック、読んだらプッシュバックでいい。

"Main.cpp"
//省略
#include <vector>
 
//省略
using std::vector;
 
void Main()
{
    //省略
	vector<string> inputList;
 
	Console.open();
	while (std::getline(input_file, indata))
	{
		cout << indata << endl;
		inputList.push_back(indata);
		getchar();
	}
	input_file.close();
    //省略
}

これで、たぶん

inputList[0] = "Dorothy lived in the midst of the great Kansas prairies, with Uncle Henry, who was a farmer, and Aunt Em, who was the farmer's wife."
inputList[1] = "Their house was small, for the lumber to build it had to be carried by wagon many miles."
inputList[2] = "There were four walls, a floor and a roof, which made one room; and this room contained a rusty looking cookstove, a cupboard for the dishes, a table, three or four chairs, and the beds."

こんなデータが出来上がる。
これを、単語区切りで取り出してみよう!
単語で取り出せたらみんなは、文字列はそのまま使えるし、数字列⇒数値 の変換はごにゃごにゃすれば、できるので後は何でもできるよね!
まず方針を立てよう。
今のデータは、じーってみてみると単語の区切りとして、“ “半角スペース、”,”半角コンマ、“.”ピリオドが使われている。
僕らの最終目標はCSVを読み込むことである。なので、今回はすこしコンマ区切りに執着してみよう
区切り文字が何種類もあると困るのでまずは、区切り文字を統一していこう
1行の文字を1文字ずつ読んでいって、

  • “ “⇒”,”の変換をする
  • ピリオドは消去

これをやってみよう。

"Main.cpp"
//vector保存まで省略
	for (auto& line : inputList)
	{
		for (int i=0;i<line.size();i++)
		{
			if (line[i] == '.')   //ピリオドは消す
				ごにょごにょ; //i番目の文字を削除 string::eraseを使用
			if (line[i] == ' ')   //スペース⇒コンマ変換
				ごにょごにょ; //スペースをコンマに置き換え 普通の代入文
		}
	}
        //変換したデータを1行ずつ表示
	for (auto& line : inputList) 
	{
		cout << line << endl;
	}
 
	input_file.close();
//以下省略

要らんデータを削除して、単語をコンマで区切ったデータができたかい?
そしたら、こんどはそれを、単語単位で表示できたら勝てるよね!

fstreamだけでも頭が爆発しそうなのに今日は大サービスでもう一個ストリームが登場する。
その名もstringstreamこれは、その名の通り文字列を入出力するためのストリームである。
iostream: 標準入出力(画面、キーボード)⇔ プログラム
fstream : file⇔プログラム
stringstream : 文字列 ⇔ プログラム
全部同じように使えるよってところがポイントだよ。
こんなことできたでしょ

string a;
int b;
char c;
cin >> a >> b >> c;
//abc 15 Z
cout << a << b << c << endl;
// abc15Z

型が違っても»や«でストリームに流したりとってきたりで適切な方へ流し込んだり、適切な型を推測して処理してくれるのよね。
同じように

#include <iostream>
#include <string>
#include <vector>
#include <sstream>
 
using std::cout;
using std::endl;
using std::string;
using std::stringstream;
 
int main()
{
	string data{ "abc 15 Z" };
	stringstream ss{ data };
	string input_a;
	int input_b;
	char input_c;
 
	ss >> input_a >> input_b >> input_c;
 
	cout << input_a << endl;
	cout << input_b << endl;
	cout << input_c << endl;
	return 0;
}

こんなことができちまうのだ!
基本的な区切り文字はスペースになっているが、これをコンマで区切れたらもう勝利は目前だよね。
次はstringstreamをつかって、コンマ区切りで文字列を分割してみる。
実はgetlineはstringstreamを受け取ることができる。したがって、前にやったように1行ごとに

#include <iostream>
#include <string>
#include <vector>
#include <sstream>
 
using std::cout;
using std::endl;
using std::string;
using std::stringstream;
 
int main()
{
	string data{ "abc,15,Z" };
	stringstream ss{ data };
	string tmp;
 
	while (getline(ss, tmp, ',')) {
		cout << tmp << endl;
	}
 
	return 0;
}

こうすれば、コンマ区切りで文字列をとってこれる。
ただこれだと、必ずいったん文字列を経由することになるので、中でもう一回stringstream経由して数値に直す。とかも必要になる。
まぁ逆に言うと、stringからならなんにでも変換できるんだからあとは頑張れ!ということでもある。

Missions
  1. プログラムを実行して、文字列分割ができているかを確認
  2. エクセルで出力したマップのCSVデータを使って同じことをしてみる
    1. マップのCSVデータをstringのままでいいので、表示してみる
    2. マップのCSVデータをstringの「想定したマップサイズの2次元配列」にぶっこんでみる
    3. マップのCSVデータをstring型からint型に変換して、今プログラム内で宣言している配列と同じものをファイル読み込みで作ってみる
    4. 読み込んだマップを表示できるようにする
  3. できた人は、マップをでかくしてみよう!

 - マップに関するいろいろな定数がおかしくならないように調整しよう(スクリーンサイズとか、ワールドサイズとか、。。。)

おまけ(OMAKE)

Siv3Dにはすんごい機能がある。なんと、いろんなアプリを使うときに出てくるファイルを選択するウィンドウ(ファイル選択ダイアログ)を1行で出せるのだ。
以下のようにすると、ダイアログで選んだファイル名をプログラム内で使うことができる。

"Main.cpp"
	Optional<FilePath> path = Dialog::OpenFile({ FileFilter::AllFiles() }); //ファイルダイアログでファイルを選択
	if (!path) //ファイル開けなかったら自爆
		exit(1);
	string fpath(path.value().narrow()); //Siv3DのOptional<String>型から、std::stringに変換
  • game-engineer/classes/2023/game-programing-1/second-term/11/11-22-zz4.txt
  • 最終更新: 2年前
  • by root