online compiler and debugger for c/c++

code. compile. run. debug. share.
Source Code   
Language
//C++17 #include <iostream> #include <string> #include <algorithm> #include <optional> #include <cassert> #include <iomanip> struct WildCardParser { //? Любой отдельный символ //* Ноль или более символов //# Любая однозначная цифра (0-9) //[charlist] Любой отдельный символ в charlist //[!charlist] Любой отдельный символ, отсутствующий в charlist //Symbol Meaning //LIKE '5[*]' 5* //LIKE '[?]n' ?n //LIKE '[a-cdf]' a, b, c, d, or f //LIKE '[-acdf]' -, a, c, d, or f //LIKE '[ [ ]' [ //LIKE ']' ] //LIKE 'abc[_]d*' abc_d and abc_de //LIKE 'abc[def]' abcd, abce, and abcf constexpr static bool ShowDebugOutput=!true; constexpr static std::wstring_view DebugPrefix=L" dbg: "; //информация о пропарсенных квадратных скобках struct s_SquareBracketsInfo { private: //#todo - для оптимизации можно попробовать заменить строку массивом, тогда уберётся работа с кучей std::wstring m_CharsList;//список символов, раскрытый из квадратных скобок size_t m_ClosingBracketPos{};//позиция закрывающей скобки bool m_Inverted{};//флаг инверсии из начала скобок private: //для оптимизации: предыдущий распарсенный текст. //Если он такой же, то заново парсить не нужно, всё уже готово std::optional<std::wstring_view> m_lastParsedText; public: bool TestChar(const wchar_t c)const noexcept { const bool contains=(m_CharsList.find(c)!=m_CharsList.npos); if(m_Inverted) { return !contains; } else { return contains; } } bool Parse(const std::wstring_view text) { if(m_lastParsedText) { if(m_lastParsedText->size()==text.size()) { if(m_lastParsedText->data()==text.data()) { if constexpr(ShowDebugOutput)std::wcout<<DebugPrefix<<L"m_lastParsedText reused\n"; return true; } } } const auto res=Parse__(text); if(res) { //запоминаем последний парсённый текст m_lastParsedText=text; //if constexpr(ShowDebugOutput)std::wcout<<DebugPrefix<<L"m_lastParsedText saved\n"; assert(m_lastParsedText); assert(!m_lastParsedText->empty()); assert(m_lastParsedText->size()>m_ClosingBracketPos); assert((*m_lastParsedText)[m_ClosingBracketPos]==L']'); } else { //поскольку состояние могло меняться, забываем последний текст m_lastParsedText.reset(); if constexpr(ShowDebugOutput)std::wcout<<DebugPrefix<<L"m_lastParsedText reset\n"; } return res; } size_t PosOfClosingBracket()const noexcept { return m_ClosingBracketPos; } private: //заполнение списка символов из квадратных скобок //text - начинается с первого символа после открывающей L'[' bool Parse__(std::wstring_view text) { //тут не производится оптимизация по удалению повторов [ababa]. //Предполагается, что юзер сам себе не враг, а если добавлять алгоритм //удаления повторов - это будет постоянная дополнительная нагрузка m_CharsList.clear(); m_ClosingBracketPos={}; m_Inverted={}; m_CharsList.reserve(256); if(text.empty())return false; //справа ограничиваемся по закрывающей скобке { const auto pos=text.find_first_of(L']'); if(pos==text.npos)return false; text=text.substr(0, pos); m_ClosingBracketPos=pos; } //флаг инверсии if(!text.empty() && text.front()==L'!') { m_Inverted=true; text.remove_prefix(1); } //сейчас text такой: //begin,front - первый символ после L'[' или L"[!" //back - символ перед L']' //end - L']' auto text_cur=text.cbegin(); auto const text_end=text.cend(); while(text_cur!=text_end) { //text_cur - текущий символ //если следующий символ это минус, if(const auto hyphen=text_cur+1; hyphen!=text_end && *hyphen==L'-') { //то после минуса тоже должен быть какой-то символ if(const auto second=hyphen+1; second!=text_end) { //обработка X-Z, сейчас находимся на X auto c1=*text_cur; auto c2=*second; if(c1>c2)std::swap(c1, c2); for(auto c=c1; c<=c2; ++c)m_CharsList.push_back(c); //идём дальше text_cur=second+1; continue; } } //один символ кладём в список m_CharsList.push_back(*text_cur); text_cur++; } return true; } }; static bool Match(const std::wstring_view text, const std::wstring_view wc) { //информация о пропарсенных квадратных скобках s_SquareBracketsInfo SBInfo; return Match_recursed(text, wc, SBInfo); } private: static bool Match_recursed(const std::wstring_view text, const std::wstring_view wc, s_SquareBracketsInfo& SBInfo) { auto wc_cur=wc.cbegin(); auto const wc_end=wc.cend(); auto text_cur=text.cbegin(); auto const text_end=text.cend(); while(wc_cur!=wc_end) { if(text_cur==text_end) { //завершающие звёздочки игнорируем if(*wc_cur==L'*') { ++wc_cur; continue; } return false; } switch(*wc_cur) { default: { //любой другой символ - должен совпасть с текстовым if(*wc_cur == *text_cur) { ++wc_cur; ++text_cur; } else { return false; } }break; case L'?': { ++wc_cur; ++text_cur; }break; case L'#': { if(L'0' <= *text_cur && *text_cur<=L'9') { ++wc_cur; ++text_cur; } else { return false; } }break; case L'[': { const auto wc_next=wc_cur+1; if(wc_next!=wc_end && SBInfo.Parse({&*wc_next, size_t(wc_end-wc_next)})) { //символ строки должен лежать в списке if(SBInfo.TestChar(*text_cur)) { wc_cur=wc_next+SBInfo.PosOfClosingBracket()+1; ++text_cur; } else { return false; } } else { //считаем обычным символом //любой другой символ - должен совпасть с текстовым if(*wc_cur == *text_cur) { ++wc_cur; ++text_cur; } else { return false; } } }break; case L'*': { const auto wc_next=wc_cur+1; if(wc_next==wc_end) { //следующего символа в маске нет, поэтому всё остальное - матчится return true; } //нужно выяснить, сколько символов возьмёт звёздочка из последующей строки. //Используем для этого рекурсию auto text_lef=text_end; while(1) { //предполагаем, что звёздочка забирает [text_lef...text_end). //Тогда эта часть строки должна сматчится с оставшейся частью маски if(Match_recursed({&*text_lef, size_t(text_end-text_lef)}, {&*wc_next, size_t(wc_end-wc_next)}, SBInfo)) { return true; } if(text_lef==text_cur) { return false; } text_lef--; } }break; } } //строка и маска должны быть дочитаны полностью return wc_cur==wc_end && text_cur==text_end; } }; int main() { struct s_test { const std::wstring_view string; const std::wstring_view mask; bool result{}; s_test()noexcept=default; s_test(const s_test&)noexcept=default; s_test(s_test&&)noexcept=default; s_test& operator=(const s_test&)noexcept=default; s_test& operator=(s_test&&)noexcept=default; s_test(const std::wstring_view string, const std::wstring_view mask, const std::wstring result) :string{string}, mask{mask} { this->result=(result==L"True"); } }; const std::vector<s_test> tests { {L"XYXZZXY" , L"*X*X?" , L"True"}, {L"aBBBa" , L"a*a" , L"True"}, {L"F" , L"[A-Z]" , L"True"}, {L"F" , L"[!A-Z]" , {} }, {L"a2a" , L"a#a" , L"True"}, {L"aM5b" , L"a[L-P]#[!c-e]" , L"True"}, {L"BAT123khg" , L"B?T*" , L"True"}, {L"CAT123khg" , L"B?T*" , {} }, {L"ab" , L"a*b" , L"True"}, {L"a*b" , L"a[*]b" , {} }, {L"axxxxxb" , L"a[*]b" , {} }, {L"a [xyz" , L"a [[]*" , L"True"}, {L"aM5b" , L"a*?b" , L"True"}, {L"aM5b" , L"a*[1-4-9][!c-e]" , L"True"}, {L"aM55b" , L"a*[1-4-9][!c-e]" , L"True"}, {L"aM5b" , L"*#[!c-e]" , L"True"}, {L"" , L"[]" , L"True"}, {L"" , L"*" , L"True"}, {L"" , L"" , L"True"}, {L"*" , L"" , {} }, {L"5*" , L"5[*]" , L"True"}, {L"?n" , L"[?]n" , L"True"}, {L"a" , L"[a-cdf]" , L"True"}, {L"b" , L"[a-cdf]" , L"True"}, {L"c" , L"[a-cdf]" , L"True"}, {L"d" , L"[a-cdf]" , L"True"}, {L"f" , L"[a-cdf]" , L"True"}, {L"-" , L"[-acdf]" , L"True"}, {L"a" , L"[-acdf]" , L"True"}, {L"c" , L"[-acdf]" , L"True"}, {L"d" , L"[-acdf]" , L"True"}, {L"f" , L"[-acdf]" , L"True"}, {L"[" , L"[ [ ]" , L"True"}, {L"]" , L"]" , L"True"}, {L"abc_d" , L"abc[_]d*" , L"True"}, {L"abc_de" , L"abc[_]d*" , L"True"}, {L"abcd" , L"abc[def]" , L"True"}, {L"abce" , L"abc[def]" , L"True"}, {L"abcf" , L"abc[def]" , L"True"}, {L"abcdef" , L"abc*[de]ef" , L"True"}, {L"abcyef" , L"abc[xz]ef" , {} }, {L"abcxef" , L"abc[xz]ef" , L"True"}, {L"abcyef" , L"abc[!xz]ef" , L"True"}, {L"abcxef" , L"abc[!xz]ef" , {} }, {L"ac5c5b" , L"a*[1-56-9][!c-e]", L"True"}, {L"a [xyz" , L"a [*" , {} }, }; size_t wid_of_string{}; size_t wid_of_mask{}; for(const auto& test:tests) { wid_of_string=(std::max)(wid_of_string, test.string.size()); wid_of_mask =(std::max)(wid_of_mask , test.mask .size()); } size_t errors_count{}; for(const auto& test:tests) { if(test.result==WildCardParser::Match(test.string, test.mask)) { std::wcout<<L"ok : "; } else { std::wcout<<L"error: "; errors_count++; } using std::left, std::setw; std::wcout<<L" string "<<left<<setw(wid_of_string+size_t(1))<<test.string; std::wcout<<L" LIKE " <<left<<setw(wid_of_mask +size_t(1))<<test.mask ; std::wcout<<"must be "<<(test.result?L"true":L"false"); std::wcout<<'\n'; if constexpr(WildCardParser::ShowDebugOutput)std::wcout<<L"\n"; } std::wcout<<L"\nerrors_count="<<errors_count<<L"\n"; return 0; }

Compiling Program...

Command line arguments:
Standard Input: Interactive Console Text
×

                

                

Program is not being debugged. Click "Debug" button to start program in debug mode.

#FunctionFile:Line
VariableValue
RegisterValue
ExpressionValue