手元で書いていたMarkdownの画像のpathをGoogle Photos上のに置き換えるやつ

# はじめに

私は普段ブログの執筆をはてなブログで行っている。

また画像はGoogle Photosにあげている。

幸いはてなブログにはGoogle Photosの画像を簡単に貼り付けることができる機能があるので非常に助かっている。

しかし画像がいくつもあるときはやっぱり手元のVSCodeとかで執筆したい。

そうしたときに画像のpathをGoogle Photosのものに置換する作業が地味につらいので自動化した。

# はてなブログのGoogle Photosの画像を簡単に貼り付けることができる機能によって吐かれるもの

画像は一度に複数選択して貼り付けられる。結果は

```html
<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)`

に置換すればよい。この作業すら自動化しても良かったのだが、この記事を書くまで忘れていた。

出力は

```markdown
![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)
```

こんなかんじだ。

# 自動置換

こうしてできた画像ファイルのリストを使って

```markdown
#### 機械の配置の見直し

いい加減`survival generator`と機械を市松模様に並べることに限界を感じてきた。前回作成した`Basic Capacitor Bank`を大幅増強し、発電機を床下に隠した。`survival generator`には常時原木を供給するように。

![img](2018-11-17_21.33.21.png)

![img](2018-11-22_20.15.36.png)
```

みたいに書かれているMarkdownを

```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)
```

みたいに置換したい。

というわけで適当にプログラムを書く。

```cpp: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;
}
```

https://wandbox.org/permlink/jYBIXo47TNAcsTdj

画像リストファイルから`2018-11-21_22.12.17.png`みたいな名前を抜き出してkeyにした辞書を作って、それをもとに照会して出力している。

# License

[CC BY 4.0](https://creativecommons.org/licenses/by/4.0/deed.ja)

<a href="https://commons.wikimedia.org/wiki/File:CC-BY_icon.svg#/media/File:CC-BY_icon.svg"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/1/16/CC-BY_icon.svg/1200px-CC-BY_icon.svg.png" alt="CC-BY icon.svg" width="88px"></a>