メモリ マップ ファイルを使用すると、実際のディスク アクセス (書き込みアクセス) を減らすことができます。一致する行が「後期」ブロックにある場合、先行するブロックのいずれかを (再) 書き込むことを避けることができます。
次のコード
- プラットフォームに依存しないようにブーストを使用します
- 一致したターゲット行よりも短いだけでなく、長い置換も可能
- ファイルのサイズをその場で変更します
- 正規表現による行の検索を示します
- ここに投稿する前に、(Linuxで)かなりよくテストされています
根拠については、インライン コメントを参照してください。コード:
#include <boost/iostreams/device/mapped_file.hpp>
#include <boost/regex.hpp>
#include <boost/filesystem.hpp>
using namespace boost;
const auto MAX_INCREASE = 1024; // allow 1 kilobyte room to grow
void process(
filesystem::path const& spec,
std::string const& pattern,
std::string const& replace)
{
// get current size of file on disk
auto const cur_size = file_size(spec);
// map, with MAX_INCREASE room to spare
iostreams::mapped_file mmap(
spec.native(),
iostreams::mapped_file::readwrite,
cur_size+MAX_INCREASE);
// find the line matching 'pattern'
char *bof = mmap.data();
char *eof = bof + cur_size; // don't read beyond cur_size!
regex re(pattern);
match_results<char*> match;
if (regex_search(bof, eof, match, re))
{
// replace the matched contents!
auto b = match[0].first,
e = match[0].second;
std::cout << "Matching line: '" << std::string(b, e) << "'\n";
// figure out whether we need to grow/shrink
auto delta = (b + replace.size()) - e;
std::cout << "Delta: " << delta << "\n";
if (delta < 0)
{
// shrinking
std::copy(replace.begin(), replace.end(), b); // replacement
std::copy(e, eof, e + delta); // shift back
resize_file(filesystem::path(spec), cur_size + delta);
}
else if (delta < MAX_INCREASE)
{
// growing
resize_file(filesystem::path(spec), cur_size + delta);
std::copy_backward(b, eof, eof + delta); // shift onwards
std::copy(replace.begin(), replace.end(), b); // insert replacement
}
else
{
// error handling (MAX_INCREASE exceeded)
}
}
// TODO error handling (no match)?
}
int main()
{
process("input.txt", "^int .*?$", "void foo()\n// mmap was here");
//process("input.txt", "^int .*?$", "");
}