13
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-04-30

前置き

話を始める前に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 | 本の虫
を見ましょう。

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

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

EF BB BF

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

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

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

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

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

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

#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に書いてあった気がする。

#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

結論

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

余談

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

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

経緯

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

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

13
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?