はじめに
私は普段ブログの執筆をはてなブログで行っている。
また画像はGoogle Photosにあげている。
幸いはてなブログにはGoogle Photosの画像を簡単に貼り付けることができる機能があるので非常に助かっている。
しかし画像がいくつもあるときはやっぱり手元のVSCodeとかで執筆したい。
そうしたときに画像のpathをGoogle Photosのものに置換する作業が地味につらいので自動化した。
はてなブログのGoogle Photosの画像を簡単に貼り付けることができる機能によって吐かれるもの
画像は一度に複数選択して貼り付けられる。結果は
<span itemtype="http://schema.org/Photograph" itemscope="itemscope"><img class="magnifiable" src="https://lh3.googleusercontent.com/-GmQ4306G_XE/W_eyP8bHFsI/AAAAAAAANm8/TgKqNX902G4gn9PZZ5u4mQqY7Y-QZuV8wCE0YBhgL/s1024/2018-11-22_20.12.35.png" itemprop="image"></span>
<span itemtype="http://schema.org/Photograph" itemscope="itemscope"><img class="magnifiable" src="https://lh3.googleusercontent.com/-ZGpYEx1Lpq0/W_eyP5o_sxI/AAAAAAAANm8/ir9FdXHJ0ksxIDSjwlSJ5pNyrkX3dKkuQCE0YBhgL/s1024/2018-11-22_20.13.05.png" itemprop="image"></span>
こんな感じのがでてくる。
Markdownに変換する
これは正規表現で十分だ。
<span itemtype="http://schema.org/Photograph" itemscope="itemscope"><img class="magnifiable" src="([^"]+)".+
で検索して
![img]($1)
に置換すればよい。この作業すら自動化しても良かったのだが、この記事を書くまで忘れていた。
出力は
![img](https://lh3.googleusercontent.com/-GmQ4306G_XE/W_eyP8bHFsI/AAAAAAAANm8/TgKqNX902G4gn9PZZ5u4mQqY7Y-QZuV8wCE0YBhgL/s1024/2018-11-22_20.12.35.png)
![img](https://lh3.googleusercontent.com/-ZGpYEx1Lpq0/W_eyP5o_sxI/AAAAAAAANm8/ir9FdXHJ0ksxIDSjwlSJ5pNyrkX3dKkuQCE0YBhgL/s1024/2018-11-22_20.13.05.png)
こんなかんじだ。
自動置換
こうしてできた画像ファイルのリストを使って
#### 機械の配置の見直し
いい加減`survival generator`と機械を市松模様に並べることに限界を感じてきた。前回作成した`Basic Capacitor Bank`を大幅増強し、発電機を床下に隠した。`survival generator`には常時原木を供給するように。
![img](2018-11-17_21.33.21.png)
![img](2018-11-22_20.15.36.png)
みたいに書かれているMarkdownを
#### 機械の配置の見直し
いい加減`survival generator`と機械を市松模様に並べることに限界を感じてきた。前回作成した`Basic Capacitor Bank`を大幅増強し、発電機を床下に隠した。`survival generator`には常時原木を供給するように。
![img](https://lh3.googleusercontent.com/-p-Yw1iRJCvA/W_eyPy-i78I/AAAAAAAANnE/CKQB6YgL0-8UWVwe1rXYcuS4lVfEAA4kgCE0YBhgL/s1024/2018-11-17_21.33.21.png)
![img](https://lh3.googleusercontent.com/-butcP0DsnFU/W_eyP8sz42I/AAAAAAAANm8/Up5fUXmITsw3WPh2o4iKfpkqXnzqaGBvQCE0YBhgL/s1024/2018-11-22_20.15.36.png)
みたいに置換したい。
というわけで適当にプログラムを書く。
C++17
#include <fstream>
#include <iostream>
#include <unordered_map>
#include <string>
#include <algorithm>
#include <sstream>
#include <limits>
#include <regex>
#include <string_view>
#include <optional>
#include <iomanip>
auto read_imglist(const char* filename)
{
std::ifstream f(filename);
std::unordered_map<std::string, std::string> re;
for(std::string buf; std::getline(f, buf); ) if(5 < buf.size() && '!' == buf.front()) {
const auto imgname_pos = buf.find_last_of('/') + 1;
const auto imgname_end_pos = buf.find_last_of(')');
if(imgname_end_pos < imgname_pos) continue;
const auto imgname_len = imgname_end_pos - imgname_pos;
auto k = buf.substr(imgname_pos, imgname_len);
re.try_emplace(std::move(k), std::move(buf));
}
return re;
}
void skip_utf8_bom(std::ifstream& fs)
{
int dst[3];
for (auto& i : dst) i = fs.get();
constexpr int utf8[] = { 0xEF, 0xBB, 0xBF };
if (!std::equal(std::begin(dst), std::end(dst), utf8)) fs.seekg(0);
}
std::string read_target(const char* filename)
{
std::ifstream f(filename);
skip_utf8_bom(f);
std::stringstream ss;
ss << f.rdbuf();
return ss.str();
}
std::optional<std::string_view> extract_img_path(std::string_view mdimg)
{
#ifdef _DEBUG
std::cerr
<< "extract_img_path()::" << std::endl
<< "input: " << mdimg << std::endl;
#endif
const auto img_pos = mdimg.find_first_of('(', mdimg.find_first_of(']')) + 1;
const auto img_pos_end = mdimg.find_last_of(')');
#ifdef _DEBUG
std::cerr << "img_pos:" << img_pos << ", img_pos_end:" << img_pos_end << std::endl;
#endif
if(img_pos_end < img_pos) return std::nullopt;
#ifdef _DEBUG
std::cerr << "result:" << mdimg.substr(img_pos, img_pos_end - img_pos) << std::endl;
#endif
return mdimg.substr(img_pos, img_pos_end - img_pos);
}
std::string replace_target(const std::string& target, const std::unordered_map<std::string, std::string>& real_img_path)
{
std::string re;
using lim = std::numeric_limits<std::size_t>;
using s = std::string;
re.reserve(lim::max() / 2 < target.size() ? lim::max() : target.size() * 2);
std::regex mdimgreg(R"(!\[[^\]]+\]\([^)]+\))");
std::size_t next_copy_front = 0;
for (std::sregex_iterator it(std::begin(target), std::end(target), mdimgreg), end; it != end; ++it){
#ifdef _DEBUG
std::cerr << "pos:" << it->position() << ", length:" << it->length() << ", s:" << it->str() << std::endl;
#endif
std::string_view mdimg(target.data() + it->position(), it->length());
const auto copy_end_pos = it->position() + it->length();
const auto img = extract_img_path(mdimg);
#ifdef _DEBUG
std::cerr << std::boolalpha << img.has_value() << std::endl;
if(img) std::cerr << img.value() << " exist in real_img_path:" << std::boolalpha << (0 != real_img_path.count(s(img.value()))) << std::endl;
#endif
if(img && real_img_path.count(s(img.value()))){
re.append(target, next_copy_front, it->position() - next_copy_front);
#ifdef _DEBUG
std::cerr
<< "replace from:" << img.value() << std::endl
<< "to:" << real_img_path.at(s(img.value())) << std::endl;
#endif
re.append(real_img_path.at(s(img.value())));
}
else{
re.append(target, next_copy_front, copy_end_pos - next_copy_front);
}
next_copy_front = copy_end_pos;
}
if(next_copy_front < target.size()) re.append(target, next_copy_front);
return re;
}
int main(int argc, char* argv[])
{
if(3 != argc){
std::cerr
<< "invalid argument" << std::endl
<< "md_img_replacer <image list text file path> <target markdown file path>" << std::endl;
return 1;
}
const auto real_img_path = read_imglist(argv[1]);
#ifdef _DEBUG
for(auto [k, v] : real_img_path){
std::cerr << "key: " << k << ", value: " << v << std::endl;
}
#endif
if(real_img_path.empty()){
std::cerr << "image list is empty" << std::endl;
return 2;
}
std::cout << replace_target(read_target(argv[2]), real_img_path) << std::flush;
}
画像リストファイルから2018-11-21_22.12.17.png
みたいな名前を抜き出してkeyにした辞書を作って、それをもとに照会して出力している。