pit-rayの備忘録

知識のあうとぷっと

【C++】ifstreamでUTF-8のBOMをスキップする方法(boost property tree)

今回は、前回とは逆で、UTF-8 with BOMを読み込むときにBOMを取り外す方法をご紹介します。

例として、boostのproperty_treeでファイルを読み込む場合を考えます。
何故なら、property_treeではBOMに対応してないため、正常にデータを読み取ることができないからです。

まず、BOMとはなにか。
前回の記事を参照してください。
www.pit-ray.com


BOMはファイルの先頭に書き込まれたデータを指します。
つまり、BOM部分をうまく読まないようにすれば、BOMなしのUTF-8として扱うことができます。


ところで先頭には、
0xEF
0xBB
0xBF

というデータが書き込まれています。

ifstreamでは、先頭から順に読んでいきます。
read関数を用いた場合は、読んだ位置まで現在位置を進めます。

この性質を生かします。


また、boostのproperty_treeのread_xml関数を見てみると以下のように、basic_istreamを引数として持つことが分かります。

// In header: <boost/property_tree/xml_parser.hpp>

template<typename Ptree> 
  void read_xml(std::basic_istream< typename Ptree::key_type::value_type > & stream, 
                Ptree & pt, int flags = 0);

(boost公式ドキュメント:Function template read_xml - 1.65.1


ifstreamは現在位置を保持するので
読み取り開始位置がBOMを読み終えた位置にある、basic_istreamを用意すればよい
という方針が立ちました。

以下のようになります。

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>

#include <fstream>

void utf8bom_to_utf8( std::ifstream& rhs )
{
    static unsigned char bom[] = { 0xEF, 0xBB, 0xBF } ;
    unsigned char buffer ;

    for( const auto& b : bom ) {
        rhs.read( reinterpret_cast<char*>( &buffer ), sizeof( buffer ) ) ;
        if( buffer == b ) {
            continue ;
        }
        else {
            rhs.seekg( 0 ) ; //bomなしの場合は、位置を先頭に戻す
            return ;
        }
    }
}

int main()
{
    std::ifstream ifs( "u8b.xml" ) ; //UTF-8 with BOMのファイル
    utf8bom_to_utf8( ifs ) ;
    boost::property_tree::ptree pt ;
    boost::property_tree::xml_parser::read_xml( ifs, pt ) ;

    //以下省略
}


プログラム自体は最適化しているわけではないので、もう少し良い設計があると思います。

参考になれば幸いです。
ご指摘等ありましたら、コメントかお問い合わせフォームにてお気軽にお寄せください。

では。