Skanerlash kutubxonasining birinchi versiyasi (0.1.0) nashr etildi. U matnni kompilyatsiya vaqti naqshiga asoslangan qiymatlarga ajratadi. Naqsh yoki format C++ shablon argumenti sifatida yoziladi va C++ shablonlari yordamida kompilyatsiya vaqti kodiga holatma-holat ochiladigan teglangan deterministik chekli avtomatga (TDFA) kompilyatsiya qilinadi. Bajarish natijasi so'ralgan turdagi qiymatdir. Loyiha kodi C++23 da yozilgan va GPLv3 litsenziyasi ostida tarqatiladi. import scan; // Butun satr mos keladimi? Har bir kirish nuqtasi ham diapazon adapteridir. scan::match<«[az]+@[az.]+»>(manzil); manzil | scan::match<«[az]+@[az.]+»>; // Guruhlar bilan. const auto found = scan::match<«([0-9]+)-([az]+)»>(«42-abc»); found.get<1>().to_view(); // "42" // Naqsh egallagan satrning boshi va istalgan joyda birinchi moslik. scan::starts_with<«[a-z]+»>(«abc123»).whole().to_view(); // "abc" scan::search<«[0-9]+»>(«id=4210x»).to_view(); // "4210" // Barcha mosliklar va ularning orasidagi qismlar dangasa ko'rinishlardir. for (const auto& one : text | scan::search_all<«[a-z]+»>) { … } const auto fields = "a,bb,,ccc" | scan::split<«,»> | std::ranges::to (); // Qiymatlar, matn emas. struct row { int id; std::string_view name; }; const row one = scan::scan<«{},{[a-z]+}»>(satr); // Ro'yxat, turlar yig'indisi, ichki shakl. struct all { std::vector qiymatlar; std::variant tail; }; const all got = scan::scan<«{{}{*,?}} {{[0-9]+}|{[a-z]+}}»>(«1,2,3 abc»); // Kiruvchi ma'lumotlarning boshlanishi va undan qolgan ma'lumotlar. const auto [value, rest] = scan::scan_prefix<«{},{}»>(satr).take (); // Bir vaqtning o'zida bitta yozuv, istalgan narsadan. for (const row& one : scan::each<«{},{[az]+}\\n»>(text).of ()) { … }
Faqat bir marta o'qilishi mumkin bo'lgan diapazon buferlashsiz o'qiladi, belgilar kelishi bilan maydonlar to'planadi. Tutilishi kerak bo'lgan belgilar soni kompilyatsiya vaqtidagi naqsh bilan belgilanadi; bu raqam mavjud bo'lmagan hollarda, kompilyatsiya vaqtida o'qish rad etiladi. 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 (manba)). ning ()) { … } Ishlash.
O'lchovlar libc++, -O3 -march=native,LTO bilan 22.1.8 ga ega Ryzen 9 9950X protsessorida amalga oshirildi. Har bir o'tishda 32 ta yozuv, yettita o'tishning medianasi. "([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 bir xil beshta maydon, har biri ikki yuz harfdan iborat scan::scan .sentinel() 57.8 ns re2c 637 ns CTRE 692 ns RE2 11751 ns "first.last@subdomain.example.com" ni tanib olish, hech narsa olinmadi scan::match .sentinel().scalar() 436 ns scan::match .scalar() 545 ns re2c 1186 ns RE2 2616 ns CTRE 14339 ns Tahlil qilinayotgan guruhlar qulashi
Turga keyingi belgi qaysi guruhga tegishli ekanligi aytiladi va baholash joyida amalga oshiriladi: hech qanday sikl iteratsiyalari saqlanmaydi, hech qanday pastki satrlar yaratilmaydi. 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 jami = 0; unsigned place = 0, marks = 0; }; static constexpr state_type begin_groups() { return {}; } static constexpr void opened_group(holat_turi& one, scan::group_at<0>) { one.place = 0; one.marks = 0; } static constexpr void closed_group(holat_turi& 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(holat_turi& one, scan::group_at<1>, char) { ++one.place; } static constexpr void push_group(holat_turi& one, scan::group_at<2>, char harfi) { one.marks += harf == 'Y' ? 2u: 1u; } static constexpr finish_groups(holat_turi& one) { return {one.total}; } }; const o'qish got = scan::scan<“value={}{[az]*}”>(“value=(__X_XX)abcdefgh”).of (); // oldim.raqam.qiymati == 12, oldim.quyruq == "abcdefgh"
Har qanday chaqiruvchi obyekti — ajratuvchi, hovuz, arena va boshqa narsalar — qiymatni yaratadigan chaqiruvga o'tkazilishi mumkin. Kontekst turi yo'qolmaydi: skaner holati va uning har bir ilgagi unga shablon sifatida kiritilishi mumkin, shuning uchun skanerda chaqiruvchi turi ko'rsatilmaydi. struct arena { std::pmr::memory_resource* where = nullptr; std::pmr::memory_resource* resource() const { return where; } }; struct numbers { std::pmr::vector qiymatlar; }; struktura ikkalasi ham { chapdagi raqamlar; o'ngdagi raqamlar; }; shablon <> struktura scan::scanner { static constexpr std::string_view naqsh() { return "([0-9]+)(?:,([0-9]+))*"; } shablon struktura holati { std::pmr::vector qiymatlar; int ishlayapti = 0; }; statik holat begin_groups() { return {}; } shablon statik holatni talab qiladi (const Told& one) { one.resource(); } start_groups(const Aytilgan va aytilgan) { return {std::pmr::vector (told.resource()), 0}; } shablon static void push_group(holat) va bitta, scan::group_at , char raqami) { one.running = one.running * 10 + (raqam - '0'); } shablon static void closed_group(holat) va bitta, scan::group_at ) { one.values.push_back(one.running); one.running = 0; } shablon statik raqamlar finish_groups(holat) one) { return {std::move(one.values)}; } }; std::pmr::monotonic_buffer_resource baytlari; const arena meniki{&bytes}; scan::scan<“{} {}”>(matn).of (mine); // ikkala joy uchun bitta scan::scan<«{} {}»>(text).of (mine, scan::default_context); // har bir joyga bittadan scan::scan<“{} {}”>(text).of ({mine, scan::default_context}); // qavs ichida bir xil const both got = scan::scan<«{} {}»>(text).with(mine); // Kontekst uning turi bo'lishi shart emas: ajratuvchi quyidagi darajaga yetadi // 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));
Hook chaqiruvchining obyektini nusxasini emas, balki o'zi qabul qiladi, shuning uchun holat uning manzilini saqlashi mumkin: kontekst chaqiruv davomida saqlanib qoladi va barcha o'qish shu chaqiruv ichida sodir bo'ladi. Boshqa xususiyatlar
- Ikki qatlam: shablon qatlami (match, starts_with, search, search_all, split) va format qatlami, bu yerda "{}" maydon bo'lib, o'rin qiymati esa turning o'zidagi maydonlar tomonidan belgilanadi.
- Perl, RE2 va CTRE tillaridagi kabi, ikki xil qoida chapdan birinchi bo'lib qo'llaniladi.
- Yuqoridagilarning barchasi doimiy ifodalarda ham ishlaydi.
- Hech qanday vaqtinchalik qidiruvlar, orqaga havolalar yoki Unicode xususiyatlari yo'q; shablonlar baytlarda ishlaydi.
- Faqat ish vaqtida ma'lum bo'lgan naqshlar qo'llab-quvvatlanmaydi.
O'rnatish
Clang va GCC bilan kompilyatsiya qilinadi. C++ modullari ixtiyoriy: demodulizer yordamchi dasturi tomonidan bir xil modullardan yaratilgan sarlavha fayllari (include/) mavjud — har bir push asosiy qayta qurish, havolalar va ularni topshirishga olib keladi. C++26 bog'lash paketlari yoqilganda yagona bog'liqlik, Boost.PFR, kerak emas. 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)
Manba: opennet.ru
