Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

前置き

話を始める前に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」の項が使える。

yumetodo
ありきたりなC++erです。最近C++書いていません(あれっ
http://yumetodo.hateblo.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした