Сканерлөө китепканасынын (0.1.0) биринчи чыгарылышы жарыяланды. Ал текстти компиляциялоо убактысынын үлгүсүнө негизделген маанилерге талдайт. Үлгү же формат C++ шаблонунун аргументи катары жазылат жана тегделген детерминисттик чектүү автоматка (TDFA) компиляцияланат, анын жүрүшү C++ шаблондорун колдонуп компиляциялоо убактысынын кодуна абал боюнча жайылтылат. Аткаруунун натыйжасы суралган типтеги маани болуп саналат. Долбоордун коду C++23 тилинде жазылган жана GPLv3 лицензиясы боюнча таратылат. import scan; // Бүтүндөй сап дал келеби? Ар бир киргизүү чекити ошондой эле диапазон адаптери болуп саналат. scan::match<«[az]+@[az.]+»>(дарек); дарек | scan::match<«[az]+@[az.]+»>; // Топтор менен. const auto found = scan::match<«([0-9]+)-([az]+)»>(«42-abc»); found.get<1>().to_view(); // "42" // Үлгү ээлеген саптын башы жана каалаган жердеги биринчи дал келүү. scan::starts_with<«[a-z]+»>(«abc123»).whole().to_view(); // "abc" scan::search<«[0-9]+»>(«id=4210x»).to_view(); // "4210" // Бардык дал келүүлөр жана алардын ортосундагы бөлүктөр жалкоо көрүнүштөр. for (const auto& one : text | scan::search_all<«[a-z]+»>) { … } const auto fields = "a,bb,,ccc" | scan::split<«,»> | std::ranges::to (); // Маанилер, текст эмес. struct row { int id; std::string_view name; }; const row one = scan::scan<«{},{[a-z]+}»>(сап); // Тизме, түрлөрдүн суммасы, ичине салынган форма. struct all { std::vector маанилер; std::variant tail; }; const all got = scan::scan<«{{}{*,?}} {{[0-9]+}|{[a-z]+}}»>(«1,2,3 abc»); // Киргизилген файлдын башы жана андан калган нерсе. const auto [value, rest] = scan::scan_prefix<«{},{}»>(сап).take (); // Бир убакта бирден жаздыруу, каалаган нерседен. for (const row& one : scan::each<«{},{[az]+}\\n»>(text).of ()) { … }
Бир гана жолу окула турган диапазон буферлөөсүз окулат, символдор келгенде талаалар чогултулат. Кармоо керек болгон символдордун саны компиляция учурундагы үлгү менен көрсөтүлөт; бул сан жок болсо, окуу компиляция учурунда четке кагылат. std::istringstream source(«set speed 42\\nset gain 7\\n»); source > std::noskipws; struct command { scan::held<16> name; int value; }; for (const command& one : scan::each<«set {[a-z]+} {[0-9]+}\\n»>(std::views::istream (булак)). ()) { … } Performance.
Өлчөөлөр Ryzen 9 9950X процессорунда, libc++, -O3 -march=native,LTO менен 22.1.8 үн сигналы менен жүргүзүлдү. Ар бир өтүү үчүн 32 жазуу, жети өтүүнүн медианасы. "([az]+),([az]+),([az]+),([az]+)" "alpha,bravo,charlie,delta,echo" scan::scan .sentinel() 477 ns re2c 554 ns scan::scan 659 ns CTRE 717 ns RE2 15986 ns ошол эле беш талаа, ар бири эки жүз тамгадан турат scan::scan .sentinel() 57.8 ns re2c 637 ns CTRE 692 ns RE2 11751 ns "first.last@subdomain.example.com" таануу, эч нерсе алынган жок scan::match .sentinel().scalar() 436 ns scan::match функциясын колдонот. .scalar() 545 нс re2c 1186 нс RE2 2616 нс CTRE 14339 нс Талданып жатканда топтордун кыйрашы
Түргө кийинки символ кайсы топторго таандык экени айтылат жана баалоо өз ордунда жүргүзүлөт: циклдин итерациялары сакталбайт, эч кандай сапчалар түзүлбөйт. struct tally { unsigned long value = 0; }; struct reading { tally number; std::string_view tail; }; template <> struct scan::scanner { static constexpr std::string_view pattern() { return R"(\\(((_+)(X|Y)*)*\\))"; } struct state_type { unsigned long total = 0; unsigned place = 0, marks = 0; }; static constexpr state_type begin_groups() { return {}; } static constexpr void opened_group(state_type& one, scan::group_at<0>) { one.place = 0; one.marks = 0; } static constexpr void closed_group(state_type& one, scan::group_at<0>) { unsigned long weight = 1; for (unsigned step = 1; step < one.place; ++step) weight *= 10; one.total += weight * one.marks; } static constexpr void push_group(state_type& one, scan::group_at<1>, char) { ++one.place; } } static constexpr void push_group(state_type& one, scan::group_at<2>, char тамгасы) { one.marks += тамга == 'Y' ? 2u: 1u; } static constexpr finish_groups(state_type one) { return {one.total}; } }; const окуусу got = scan::scan<“value={}{[az]*}”>(“value=(__X_XX)abcdefgh”).of (); // саны.мааниси == 12, куйругу == "abcdefgh"
Чакыруучунун каалаган объектиси — бөлүштүргүч, пул, арена, эмне болсо да — маанини түзгөн чакырууга өткөрүлүп берилиши мүмкүн. Контексттин түрү жоголбойт: сканердин абалы жана анын ар бир илгичи ага шаблон түрүндө түзүлүшү мүмкүн, ошондуктан сканерде чакыруучунун түрү айтылбайт. struct arena { std::pmr::memory_resource* where = nullptr; std::pmr::memory_resource* resource() const { return where; } }; struct numbers { std::pmr::vector маанилер; }; структура экөө тең { сол жактагы сандар; оң жактагы сандар; }; шаблон <> структураны сканерлөө::сканер static constexpr std::string_view үлгүсү () { return "([0-9]+)(?:,([0-9]+))*"; } шаблон структуралык абал { std::pmr::вектор маанилер; int иштеп жатат = 0; }; статикалык абал begin_groups() { return {}; } шаблон } ... begin_groups(const Told& told) { return {std::pmr::vector} (told.resource()), 0}; } шаблон static void push_group(абал жана бир, scan::group_at , символдук сан) { бир.чуркап жатат = бир.чуркап жатат * 10 + (цифр - '0'); } шаблон static void closed_group(абал жана бир, scan::group_at ) { one.values.push_back(one.running); one.running = 0; } шаблон статикалык сандар finish_groups(state бир) { return {std::move(one.values)}; } }; std::pmr::monotonic_buffer_resource байттары; const arena mine{&bytes}; scan::scan<“{} {}”>(текст).of (меники); // эки жер үчүн тең бирөө scan::scan<«{} {}»>(текст).of (меники, scan::default_context); // ар бир жерге бирден scan::scan<“{} {}”>(текст).of ({mine, scan::default_context}); // кашаанын ичинде бирдей const both got = scan::scan<«{} {}»>(text).with(mine); // Контексттин түрү болушу шарт эмес: бөлгүч төмөнкүгө чейин барат // read.struct two { std::pmr::string name; std::pmr::string tail; }; const two kept = scan::scan<«{[az]+} {[az]+}»>(text).of<( std::pmr::polymorphic_allocator<>(&bytes));
Илмек чалуучунун объектисинин көчүрмөсүн эмес, өзүн алат, ошондуктан абал анын дарегин сактай алат: контекст чакыруу болгончо жашайт жана бардык окуу ошол чакыруунун ичинде болот. Башка өзгөчөлүктөрү
- Эки катмар: шаблон катмары (дал келүү, менен_баштоо, издөө, издөө_баары, бөлүү) жана формат катмары, мында "{}" талаа, ал эми орун мааниси түрдүн өзүнүн талаалары менен аныкталат.
- Көп маанини ажыратуу эрежеси Perl, RE2 жана CTRE тилдериндегидей эле, эң сол жак биринчиде.
- Жогоруда айтылгандардын баары туруктуу туюнтмаларда да иштейт.
- Эч кандай айланып өтүү ыкмалары, шилтемелер же Unicode касиеттери жок; шаблондор байттар менен иштейт.
- Иштөө учурунда гана белгилүү болгон үлгүлөр колдоого алынбайт.
жыйналыш
Clang жана GCC менен компиляцияланат. C++ модулдары милдеттүү эмес: демодулайзер утилитасы тарабынан ошол эле модулдардан түзүлгөн баш файлдар (include/) жеткиликтүү — ар бири негизги кайра курууларга, шилтемелерге жана аларды коммитациялоого түртөт. C++26 байланыштыруу пакеттери иштетилгенде жалгыз көз карандылык, Boost.PFR, кереги жок. FetchContent_Declare(scan GIT_REPOSITORY https://github.com/j4niwzis/scan.git GIT_TAG v0.1.0) FetchContent_MakeAvailable(scan) target_link_libraries(mine PRIVATE scan::scan)
Source: opennet.ru
