boost::xpressiveのrangeでハマる
boost::xpressiveを使って「コントロール文字を除いたUS-ASCII文字(0x20〜0x7e)」以外か判定しようとしたら、かなり恥ずかしい間違いをしたのでメモ。
元々次のような正規表現で判定をしようと思っていた。
#include <iostream> #include <boost/xpressive/xpressive.hpp> int main() { using namespace boost::xpressive; // [^\x20-\x1e] でいいじゃないかというツッコミはなしで sregex re = range('\x00', '\x1f') | range('\x7f', '\xff'); std::cout << "0x1f: 1 == " << regex_match(std::string("\x1f"), re) << std::endl; std::cout << "0x20: 0 == " << regex_match(std::string("\x20"), re) << std::endl; std::cout << "0x7e: 0 == " << regex_match(std::string("\x7e"), re) << std::endl; std::cout << "0x7f: 1 == " << regex_match(std::string("\x7f"), re) << std::endl; std::cout << "0x80: 1 == " << regex_match(std::string("\x80"), re) << std::endl; return 0; }
すると、結果は次のようになった。
0x1f: 1 == 1 0x20: 0 == 0 0x7e: 0 == 0 0x7f: 1 == 0 0x80: 1 == 0
最初はマルチバイト周りのバグかとも思ったが、よくよく考えるとcharは符号付きなので、'\x7f'==127、 '\x80'==-128、 '\xff'==-1 になるんだった。
つまり、range('\x7f', '\xff') は (127<=c && c<=-1) の判定をしていることになる。
確かに 0x7f(127) も 0x80(-128) もこの条件を満たせない。
というわけで正しくは以下のようにしなければならなかった。
sregex re = range('\x00', '\x1f') | '\x7f' | range('\x80', '\xff');
いやはや、今更符号周りでハマるとは思わなかった。