概要
可変長かつ1行しかないCSVテキストファイルを、ほぼリアルタイムに更新したい。
close() → open(path) すれば中身を消せるが、中身が消える瞬間がある。
というのを、無理矢理マシにしたメモ。
方法
一時ファイルに書き込み、それをリネーム&上書きしました。
これでも、一瞬OSがファイルをロックしてしまいますが、中身が消えてるよりいいでしょう。
(ちなみに、固定長であれば、オープンしたままファイル先頭に seekp() するだけでいいいはず。)
環境は Visual Studio 2017のVC++(C++)です。
#include <fstream>
#include <string>
#include <Windows.h>
using namespace std;
int main()
{
ofstream ofs;
string tmp_path = "./hoge_tmp";
string out_path = "./hoge.csv";
ofs.open(tmp_path);
if (!ofs) {
return 1;
}
int i = 0;
while (1) {
string str = to_string(i) + "," + to_string(i + 1);
ofs << str << endl;
ofs.close();
BOOL ret;
ret = MoveFileEx(tmp_path.c_str(), out_path.c_str(), MOVEFILE_REPLACE_EXISTING);
// MoveFileEx()は以下でも可。以下の方がアトミックである信用度が高いが、
// dst,src双方にファイルが無いと、ファイルが見つからないエラーとなる。
// また、MoveFileEx()と、dst,srcが逆な事にも注意。
// 実験した結果、ReplaceFile() は一瞬dst側を削除している事が判明。
// 詳細は本ページのコメント参照。
// ret = ReplaceFile(out_path.c_str(), tmp_path.c_str(), NULL, 0, NULL, NULL);
if (ret == 0) {
return 1;
}
ofs.open(tmp_path);
if (!ofs) {
return 1;
}
i++;
Sleep(0); // ほぼウェイト無し無限ループ
}
return 0;
}
ファイルの現在状況は、コマンドプロンプトから以下のコマンドで確認できます。
type hoge.csv
コマンドプロンプトは、「↑」→「Enter」で前回を同じコマンドを実行できるので、「↑」→「Enter」→「↑」→「Enter」→ .. と繰り返せば、連続して開いてみることが出来ます。
close() → open() の場合は、連打するとよく空データが見えていましたが、本ページのコードにすると、時折アクセス拒否されるものの、空データが見えることは無くなりました。
※ 2019/11/08-1 MoveFileEx()の戻値チェック抜けていたので追記しました。
※ 2019/11/08-2 最後に補足つけたしました。
※ 2019/11/08-3 コード中にReplaceFile()についてコメント記載しました。コメント頂いた方ありがとうございました。
※ 2019/11/08-4 ReplaceFile()の挙動実験結果をコメントに追記しました。