//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;
}