C++のstreamでbom skipする方法をまた忘れないうちに書き留める

# 前置き
## 話を始める前にBOMとは

バイトオーダーマーク (byte order mark)のことです。間違ってもBill of materialsとか東方シューティングのスペカでは無いです。

Unicodeを知らないプログラマは流石に存在しないと思うので割愛して、BOMとは一般にはファイルの先頭につけるもので

- ``UTF-8``/``UTF-16``/``UTF-32``
- ``little endianness``/``big endianness ``(UTF-8は除く)

を判別するためにあります。やべぇ、わからん、という人は
[UTF-32 is NOT a fixed-legnth character encoding | 本の虫](http://cpplover.blogspot.jp/2013/09/utf-32-is-not-fixed-legnth-character.html)
を見ましょう。

## 世の中のテキストファイルは大体UTF-8 with BOMだ(という幻想)

という幻想を信じるならバイナリエディタで覗くと

```
EF BB BF
```

でファイルが始まることになります。

ところでみんな、C++のソースコードはもちろんUTF-8 with BOMだよね？間違ってもShift-JISじゃないよね？(きっとそれをつかう職場もあるだろうな)

## ところでC++でファイルってどう開くよ？

まさかfopenを使うC++erはいないと信じて(きっとそれをつかう職場もあるだろうな)

```cpp
#include <fstream>
int main()
{
    std::ifstream file("arikitari_na_text.txt");
    if(!file) return -1;
    //なんか
}
```

# これまで私がやってたbom skip(UTF-8 only)

```cpp
#include <fstream>
#include <algorithm>
#include <codecvt>
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);
}
int main()
{
    std::ifstream file;
    file.open("arikitari_na_text.txt");
    file.imbue(std::locale());
    skip_utf8_bom(file);
    if(!file) return -1;
    //なんか
}
```

この方法はwfstreamに適応できないので、それを使うには一回bom skipしてcode_cvt噛ませつつwstringstreamにいれて、とかいうよくわかんないことをやってた。

# 今回紹介する方法

ネタ元は忘れた。どっかのStack Overflowに書いてあった気がする。

```cpp
#include <fstream>
#include <codecvt>
int main()
    std::wifstream file;
    file.open("arikitari_na_text.txt");
    static_assert(sizeof(wchar_t) == 2, "error.");//Linuxではつかうcvt違うから直してくれ
    file.imbue(std::locale(std::locale(""), new std::codecvt_utf8_utf16<wchar_t, 0x10ffff, std::consume_header>()));
    if(!file) return -1;
    //なんか
}
```

std::fstream::imbue()に渡すlocaleに渡すcodecvtのtemplate第三引数に``std::consume_header``を渡すのが肝です。

![bom skip image](https://pbs.twimg.com/media/CeZCtZtVIAQI4wP.jpg)

# 結論

C++11が使える環境ではbom skipも幸せだということです。いい加減C++03は卒業しましょう。(きっとできない職場もあるだろうな)

# 余談

試してないけど、BOMを逆に書き出すには``std::generate_header``っていうのがあるっぽいよ。

にしてもそろそろ標準で自動文字コード判別してくれないかな・・・。BOMあるときだけでいいからさ

# 経緯
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr"><a href="https://twitter.com/yumetodo">@yumetodo</a> picojsonの前にcsvデータの方をUTF-8にしようとしたら、ifsからgetlineでstringに読みこんだデータが壊れているせいでSplitしようがないのですけれど、この場合はどうしましょう？ <a href="https://t.co/2wVqtGdswL">pic.twitter.com/2wVqtGdswL</a></p>&mdash; YSR@孤高の人間観察好き㌠ (@YSRKEN) <a href="https://twitter.com/YSRKEN/status/703605271227031552">2016年2月27日</a></blockquote>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="ja"><p lang="ja" dir="ltr"><a href="https://twitter.com/YSRKEN">@YSRKEN</a> そのまま問題なく読み込めていると思います。(BOMskipしてるなら)<br>VSのデバッガはchar幅の変数はshft-JISとして表示するのでバグって見えるだけじゃないかと。<br>そのまま読み込んでwstring_convertすればいいと思います</p>&mdash; yumetodo-C++erだけど化学科 (@yumetodo) <a href="https://twitter.com/yumetodo/status/703607096772685825">2016年2月27日</a></blockquote>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="ja"><p lang="ja" dir="ltr"><a href="https://twitter.com/yumetodo">@yumetodo</a> つまり読み込んだら速攻でwstring_convertしてから、wstring前提で作った関数に突っ込んでよしなにですが……作業が一段落したらやってみます</p>&mdash; YSR@孤高の人間観察好き㌠ (@YSRKEN) <a href="https://twitter.com/YSRKEN/status/703607418341601280">2016年2月27日</a></blockquote>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="ja"><p lang="ja" dir="ltr"><a href="https://twitter.com/YSRKEN">@YSRKEN</a> はい<br>wifstreamのimubleにcodecvt指定する方法もあるのですが、bom skipが面倒になるのでおすすめしません。</p>&mdash; yumetodo-C++erだけど化学科 (@yumetodo) <a href="https://twitter.com/yumetodo/status/703608117595013120">2016年2月27日</a></blockquote>
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr"><a href="https://twitter.com/yumetodo">@yumetodo</a> まさかBOMに対応できないとは思いませんでしたもの</p>&mdash; YSR@孤高の人間観察好き㌠ (@YSRKEN) <a href="https://twitter.com/YSRKEN/status/708725409454563328">2016年3月12日</a></blockquote>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="ja"><p lang="ja" dir="ltr"><a href="https://twitter.com/YSRKEN">@YSRKEN</a> <a href="https://twitter.com/yumetodo">@yumetodo</a> VC++のfopenにはBOM対応とかエンコーディング指定とかの拡張があるんですが、iostreamは構造上、拡張できませんからねぇ…。fopenで開いてからiostreamに渡すとかぐらいしか <a href="https://t.co/VRV02aat4X">https://t.co/VRV02aat4X</a></p>&mdash; はぇ～☆ (@haxe) <a href="https://twitter.com/haxe/status/708754657376505856">2016年3月12日</a></blockquote>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="ja"><p lang="ja" dir="ltr"><a href="https://twitter.com/haxe">@haxe</a> <a href="https://twitter.com/YSRKEN">@YSRKEN</a> 別にKCS_KAIでやっているように、BOMskip書くのは全然難しくないですよ、istreamならね。なおwistreamは(ry</p>&mdash; yumetodo-C++erだけど化学科 (@yumetodo) <a href="https://twitter.com/yumetodo/status/708838794326192128">2016年3月13日</a></blockquote>
<blockquote class="twitter-tweet" data-conversation="none" data-lang="ja"><p lang="ja" dir="ltr"><a href="https://twitter.com/yumetodo">@yumetodo</a> <a href="https://twitter.com/YSRKEN">@YSRKEN</a> 肝心なのはBOM skipだけでなく、UTF-16、UTF-8の自動デコードですよ。そんなのライブラリでやってほしいものです。</p>&mdash; はぇ～☆ (@haxe) <a href="https://twitter.com/haxe/status/708842320813101060">2016年3月13日</a></blockquote>
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">std::codecvt_utf8_utf16のtemplate引数の3番めに<br>std::codecvt_modeなるものが指定できて<br>std::consume_headerを指定するとBOMskipしてくれる！！！！！ <a href="https://t.co/ZvKMJj3DsC">pic.twitter.com/ZvKMJj3DsC</a></p>&mdash; yumetodo-C++erだけど化学科 (@yumetodo) <a href="https://twitter.com/yumetodo/status/713327049260146690">2016年3月25日</a></blockquote>
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">いやぁ。これでUTF-8withBOMなファイルのBOMskipのためにわざわざ一回wstringstreamに読み込まなくてすむ！</p>&mdash; yumetodo-C++erだけど化学科 (@yumetodo) <a href="https://twitter.com/yumetodo/status/713328421971963904">2016年3月25日</a></blockquote>
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">いやぁ、素晴らしいね。これでBOMskip面倒だからと勧めづらかったWindows環境でのUTF-8withBOMなファイルのIOも薦められる！</p>&mdash; yumetodo-C++erだけど化学科 (@yumetodo) <a href="https://twitter.com/yumetodo/status/713329212321468416">2016年3月25日</a></blockquote>
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">祝！完全勝利！<br>バイナリエディタで見えるBOM(EFBBBF)がskipされて読み込めているのがわかる <a href="https://t.co/IQwtXWfblv">pic.twitter.com/IQwtXWfblv</a></p>&mdash; yumetodo-C++erだけど化学科 (@yumetodo) <a href="https://twitter.com/yumetodo/status/713330939623936000">2016年3月25日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

# 追記: C++17時代での対処

`std::codecvt_utf8_utf16`はdeprecatedになっているので、そもそもwchar_t系を利用しないで内部的にもUTF-8で扱うということでしょうね。そうすれば「これまで私がやってたbom skip」の項が使える。