2022年3月31日木曜日

datetime の 曖昧なフォーマットに対応する忘備録

日付のフォーマットと制約が厳しい。
わりとありそうな日付の表記ゆれをパースするグラマーを構築してみた。
#include <boost/spirit/include/qi.hpp>  // boost::spirit::qi を利用します
#include <boost/fusion/adapted/struct/adapt_struct.hpp>
#include <iostream>
#include <string>


#include <boost/date_time.hpp>

namespace pt = boost::posix_time;
namespace qi = boost::spirit::qi;


struct timeinfo {
  int16_t   year_;
  uint16_t  month_;
  uint16_t  day_;
  uint16_t  hour_;
  uint16_t  minute_;
  uint16_t  second_;
  //uint32_t  millisec_;
  std::string millisec_;
};

BOOST_FUSION_ADAPT_STRUCT(
  timeinfo,
  (int16_t,     year_)
  (uint16_t,    month_)
  (uint16_t,    day_)
  (uint16_t,    hour_)
  (uint16_t,    minute_)
  (uint16_t,    second_)
  //(uint32_t,    millisec_)
  (std::string,  millisec_)
)

template <typename Iterator, typename Skipper>
struct time_grammar : qi::grammar<Iterator, timeinfo(), Skipper> {
  qi::rule<Iterator, timeinfo(), Skipper>  start_;
  qi::int_parser<int16_t,10,4,4> int4_;
  qi::uint_parser<uint16_t,10,2,2> uint2_;
  qi::rule<Iterator, std::string(), Skipper> numeric_;

  time_grammar() : time_grammar::base_type(start_, "time_grammar") {
    numeric_ = *qi::char_('0','9');
    start_ = (
        int4_ 
        >> -(qi::lit('/') | qi::lit('-')) >> uint2_
        >> -(qi::lit('/') | qi::lit('-')) >> uint2_
        //>> (qi::lexeme[qi::lit(' ')] | qi::lit('T'))
        >> -(qi::lit('T'))
        >> uint2_
        >> -(qi::lit(':')) >> uint2_
        >> -(qi::lit(':')) >> uint2_
        >> (
          //-( (qi::lit('.') | qi::lit(',')) >> qi::uint_)
          -( (qi::lit('.') | qi::lit(',')) >> numeric_)
          | qi::attr(0)
        )
        >> -(qi::lit('Z'))
    );
  }
};



int main() {
  
  std::string input[] = {
    "20210412T123455Z",
    "20210412T123455,987654321Z",
    "20210412T123455.987654321Z",
    "2004-03-21 12:45:33",
    "2004/03/21 12:45:33",
    "23.09.2004 04:12:21",
    "2003-02-11"
  };

  time_grammar<std::string::iterator, qi::standard::space_type> myg;
  for( int i = 0; i < sizeof(input)/sizeof(input[0]); i++ ) {
    timeinfo ti;
    qi::phrase_parse( input[i].begin(), input[i].end(), myg, qi::standard::space, ti );
    std::cout << "--------------" << std::endl;
    std::cout << ti.year_ << std::endl;
    std::cout << ti.month_ << std::endl;
    std::cout << ti.day_ << std::endl;
    std::cout << ti.hour_ << std::endl;
    std::cout << ti.minute_ << std::endl;
    std::cout << ti.second_ << std::endl;
    std::cout << ti.millisec_ << std::endl;
  }
  return 0;
}
2023/06/02 追記: lexeme がうまく動作しないのでスペースを無視した。millisec が ,020Z と指定された場合 2 に変換されてしまうので std::string で扱うようにした。バグでした。

2022年3月28日月曜日

boost::log windows で /dev/null 忘備録

boost::log でデバッグ用ログの埋め込みを行っていて、リリース時には取り除きたい場合の措置。
これだと、nul デバイスに対して出力する処理が入るので、負荷を取り除くという面では、よろしくないコード

ポイントは、
Windows では nul というファイルが /dev/null に相当する
nul というファイルは、どこにでも存在している扱いになるので、追加書き込みモードで開く必要がある点

#include <boost/date_time.hpp>

#include <boost/log/trivial.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/attributes.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/support/date_time.hpp>

namespace logging = boost::log;
namespace attrs = boost::log::attributes;
namespace keywords = boost::log::keywords;
namespace sinks = boost::log::sinks;
namespace expr  = boost::log::expressions;

...

//#define DEBUG_LOG_FILE  "c:/logs/%Y_%m_%d.log"

...

  boost::log::add_file_log(
#ifdef DEBUG_LOG_FILE
    keywords::file_name = DEBUG_LOG_FILE,
#else
    keywords::file_name = "nul",
    keywords::open_mode = std::ios_base::out | std::ios_base::app,
#endif
    keywords::time_based_rotation = sinks::file::rotation_at_time_point(0, 0, 0),
    keywords::format = 
    (
        expr::stream
            << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
            << ": <" << logging::trivial::severity
            << "> " << expr::smessage
    ),
    keywords::auto_flush = true
  );
  BOOST_LOG_TRIVIAL(debug) << "this is a debug output.";

...