Skan kitabxanasının ilk buraxılışı (0.1.0) dərc edilib. Mətni kompilyasiya vaxtı şablonuna əsasən dəyərlərə ayırır. Şablon və ya format C++ şablon arqumenti kimi yazılır və C++ şablonlarından istifadə edərək kompilyasiya vaxtı koduna çevrilən etiketli deterministik sonlu avtomata (TDFA) kompilyasiya edilir. İcra nəticəsi tələb olunan tipin dəyəridir. Layihə kodu C++23 dilində yazılıb və GPLv3 lisenziyası altında paylanır. import scan; // Bütün sətir uyğun gəlirmi? Hər giriş nöqtəsi də diapazon adapteridir. scan::match<«[az]+@[az.]+»>(ünvan); ünvan | scan::match<«[az]+@[az.]+»>; // Qruplarla. const auto found = scan::match<«([0-9]+)-([az]+)»>(«42-abc»); found.get<1>().to_view(); // "42" // Nümunə ilə tutulmuş sətrin başlanğıcı və istənilən yerdəki ilk uyğunluq. scan::starts_with<«[a-z]+»>(«abc123»).whole().to_view(); // "abc" scan::search<«[0-9]+»>(«id=4210x»).to_view(); // "4210" // Bütün uyğunluqlar və aralarındakı hissələr tənbəl görünüşlərdir. for (const auto& one : text | scan::search_all<«[a-z]+»>) { … } const auto fields = "a,bb,,ccc" | scan::split<«,»> | std::ranges::to (); // Dəyərlər, mətn deyil. struct row { int id; std::string_view name; }; const row one = scan::scan<«{},{[a-z]+}»>(sətir); // Siyahı, növlərin cəmi, iç-içə forma. struct all { std::vector dəyərlər; std::variant tail; }; const all got = scan::scan<«{{}{*,?}} {{[0-9]+}|{[a-z]+}}»>(«1,2,3 abc»); // Girişin başlanğıcı və ondan qalanlar. const auto [dəyər, istirahət] = scan::scan_prefix<«{},{}»>(sətir).take (); // İstənilən yazıdan bir qeyd. for (const row& one : scan::each<«{},{[az]+}\\n»>(text).of ()) { … }
Yalnız bir dəfə oxuna bilən diapazon, simvollar gəldikcə toplanan sahələrlə buferləşdirmədən oxunur. Saxlanılacaq simvolların sayı kompilyasiya zamanı şablonla müəyyən edilir; bu rəqəm mövcud olmadığı təqdirdə, kompilyasiya zamanı oxumaq rədd edilir. 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 (mənbə)). of ()) { … } Performans.
Ölçmələr libc++, -O3 -march=native,LTO ilə 22.1.8 clang 22.1.8 Ryzen 9 9950X prosessorunda aparılmışdır. Hər keçiddə 32 yazı, yeddi keçidin medianı. "([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 eyni beş sahə, hər biri iki yüz hərf scan::scan .sentinel() 57.8 ns re2c 637 ns CTRE 692 ns RE2 11751 ns "first.last@subdomain.example.com" tanınması, heç nə alınmadı scan::match .sentinel().scalar() 436 ns scan::match .scalar() 545 ns re2c 1186 ns RE2 2616 ns CTRE 14339 ns Təhlil edildikcə qrupların dağılması
Növbəti simvolun hansı qrupa aid olduğu tipə deyilir və qiymətləndirmə yerində aparılır: heç bir döngə iterasiyası yadda saxlanılmır, heç bir altsətir yaradılmır. 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 cəm = 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 letter) { one.marks += letter == 'Y' ? 2u: 1u; } static constexpr tally finish_groups(state_type one) { return {one.total}; } }; const oxunuş got = scan::scan<“value={}{[az]*}”>(“value=(__X_XX)abcdefgh”).of (); // al.number.value == 12, al.quyruq == "abcdefgh"
İstənilən çağırıcı obyekti — ayırıcı, hovuz, arena və ya hər hansı bir şey — dəyəri yaradan çağırışa ötürülə bilər. Kontekst növü itirilmir: skaner vəziyyəti və onun hər bir qarmaqları şablonlaşdırıla bilər, beləliklə skanerdə heç bir çağırıcı növü qeyd olunmur. struct arena { std::pmr::memory_resource* where = nullptr; std::pmr::memory_resource* resource() const { return where; } }; struct numbers { std::pmr::vector dəyərlər; }; struktur hər ikisi { soldakı rəqəmlər; sağdakı rəqəmlər; }; şablon <> struktur scan::scanner { static constexpr std::string_view pattern() { qaytarır "([0-9]+)(?:,([0-9]+))*"; } şablon struktur vəziyyəti { std::pmr::vector dəyərlər; int işləyir = 0; }; statik vəziyyət begin_groups() { return {}; } şablonu tələb edir requires(const Told& one) { one.resource(); } statik vəziyyət begin_groups(const Dedi və dedi) { return {std::pmr::vector} (told.resource()), 0}; } şablonu static void push_group(vəziyyət və bir, scan::group_at , simvol rəqəmi) { one.running = one.running * 10 + (rəqəm - '0'); } şablon static void closed_group(state və bir, scan::group_at ) { one.values.push_back(one.running); one.running = 0; } şablon statik ədədlər finish_groups(state) one) { return {std::move(one.values)}; } }; std::pmr::monotonic_buffer_resource baytları; const arena mine{&bytes}; scan::scan<“{} {}”>(mətn).of (mine); // hər iki yer üçün bir scan::scan<«{} {}»>(text).of (mine, scan::default_context); // hər yerə bir scan::scan<“{} {}”>(text).of ({mine, scan::default_context}); // mötərizədə eyni const both got = scan::scan<«{} {}»>(text).with(mine); // Kontekstin onun növü olması vacib deyil: ayırıcı aşağıdakılara qədər gedir // oxumaq üçün bir fayl qurur. struct two { std::pmr::string name; std::pmr::string tail; }; const two kept = scan::scan<«{[az]+} {[az]+}»>(text).of<( std::pmr::polymorphic_allocator<>(&bytes));
Hook, zəng edənin obyektinin surətini deyil, özünü qəbul edir, buna görə də state onun ünvanını saxlaya bilər: kontekst zəng müddətində yaşayır və bütün oxuma həmin zəng daxilində baş verir. Digər Xüsusiyyətlər
- İki təbəqə: şablon təbəqəsi (match, starts_with, search, search_all, split) və format təbəqəsi, burada "{}" bir sahədir və yer dəyəri növün öz sahələri ilə müəyyən edilir.
- Perl, RE2 və CTRE-də olduğu kimi, ikimənalılıq qaydası ən soldan birincidir.
- Yuxarıda göstərilənlərin hamısı sabit ifadələrdə də işləyir.
- Baxışlar, geri istinadlar və ya Unicode xüsusiyyətləri yoxdur; şablonlar baytlarla işləyir.
- Yalnız icra zamanı məlum olan nümunələr dəstəklənmir.
Məclis
Clang və GCC ilə kompilyasiya edir. C++ modulları isteğe bağlıdır: demodulizer yardımçı proqramı tərəfindən eyni modullardan yaradılan başlıq faylları (include/) mövcuddur — hər bir push əsas yenidənqurmalara, bağlantılara və onları commit etməyə aparır. Yeganə asılılıq olan Boost.PFR, C++26 bağlama paketləri aktiv olduqda lazım deyil. 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)
Mənbə: opennet.ru
