1
2

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 3 years have passed since last update.

VC++でのstd::regexにおける"\D"の実装がおかしい

Last updated at Posted at 2021-05-04

C++で正規表現を使っていた時にバグにハマったので書き残し

バグ発見に至った経緯

某🐎ゲー向けのソフトを開発中に、OCRで読み取った文字列の正規化を行うために正規表現(std::regex)を使ったのだが、なぜか本来ならマッチするはずの文字列がマッチせず、書いたコードが動かなかった

# include <iostream>
# include <regex>
# include <string>

int main() {

    const std::wstring turn = L"ジュニア紹7月得半";    // turn にはOCRで読み取った正確性に欠く文字列が入っている
    std::wregex rx(LR"((ジュニア|クラシック|シニア)\D+(\d+)月(前|\W)半)");
    std::wsmatch result;
    if (std::regex_match(turn, result, rx)) {
        // "前"は認識しやすいが、"後"は認識が難しいので
        std::wstring half = result[3].str() == L"前" ? L"前半" : L"後半";
        std::wstring guessTurn = result[1].str() + L"級" + result[2].str() + L"月" + half;
        std::cout << "match!" << std::endl;
    } else {
        std::cout << "no match" << std::endl;
    }
}

だいたいこういったコードを書いていたわけだ
OCRで読み取った文字列は完璧ではないので、正確に読み取れる部分だけを抜き出して再構築してあげる必要があった
このコードをvisual studio 2019で実行してみると"no match"と表示されてしまう

https://regex101.com/
などの正規表現のテストできるサイトでチェックしてみても、自分が書いた正規表現で問題なくマッチしてくれるので、これはおかしいぞとなった

で、いろいろ試行錯誤してみるとどうやら"\D"が怪しいことが分かった
正規表現の"\D"は"[^0-9]"と同じであり、数字以外にマッチするマッチングパターンだ
"紹"は数字ではないので、本来ならマッチするのが正しいが

# include <iostream>
# include <regex>
# include <string>

int main()
{
    bool a = std::regex_search(L"あ", std::wregex(LR"(\D)"));// false
    bool b = std::regex_search(L"A", std::wregex(LR"(\D)"));// true
    bool c = std::regex_search(L"あ", std::wregex(LR"([^0-9])"));// true
    bool d = std::regex_search(L"A", std::wregex(LR"([^0-9])"));// true

    std::cout << std::boolalpha
        << "あ == \\D: " << a << "\n"
        << "A  == \\D: " << b << "\n"
        << "あ == [^0-9] : " << c << "\n"
        << "A  == [^0-9] : " << d << "\n" 
        << std::endl;

    return 0;

}

試しにこういうコードを書いてみると、 "a"がtrueにならない…
つまり、VC++の実装では"\D"は"[^0-9]"と同じではない

ちなみに他のコンパイラ(clang,gcc)では "a"はtrueとなるので、VC++だけがおかしい
https://wandbox.org/permlink/wmDPndG53xwsWZ73

バグ回避

VC++のstd::regexにおける"\D"の実装がバグってるのは分かったので、"[^0-9]"と書き直してバグを回避した

std::wregex rx(LR"((ジュニア|クラシック|シニア)[^0-9]+(\d+)月(前|\W)半)");

バグ報告

問題の再現が出来たので一応バグの報告しておいた
直るといいね!

1
2
1

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
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?