Rilis pertama pustaka scan (0.1.0) telah diterbitkan. Pustaka ini menguraikan teks menjadi nilai berdasarkan pola waktu kompilasi. Pola atau format ditulis sebagai argumen templat C++ dan dikompilasi menjadi automata hingga deterministik bertanda (TDFA), yang penelusurannya diuraikan secara bertahap menjadi kode waktu kompilasi menggunakan templat C++. Hasil eksekusi adalah nilai dari tipe yang diminta. Kode proyek ditulis dalam C++23 dan didistribusikan di bawah lisensi GPLv3. import scan; // Apakah seluruh string cocok. Setiap titik masuk juga merupakan adaptor rentang. scan::match<«[az]+@[az.]+»>(address); address | scan::match<«[az]+@[az.]+»>; // Dengan grup. const auto found = scan::match<«([0-9]+)-([az]+)»>(«42-abc»); found.get<1>().to_view(); // "42" // Awal string yang ditempati oleh pola, dan kecocokan pertama di mana saja. scan::starts_with<«[a-z]+»>(«abc123»).whole().to_view(); // "abc" scan::search<«[0-9]+»>(«id=4210x»).to_view(); // "4210" // Semua kecocokan dan bagian di antaranya adalah tampilan malas. for (const auto& one : text | scan::search_all<«[a-z]+»>) { … } const auto fields = "a,bb,,ccc" | scan::split<«,»> | std::ranges::to (); // Nilai, bukan teks. struct row { int id; std::string_view name; }; const row one = scan::scan<«{},{[a-z]+}»>(line); // Daftar, jumlah tipe, bentuk bersarang. struct all { std::vector nilai; std::varian ekor; }; const all got = scan::scan<«{{}{*,?}} {{[0-9]+}|{[a-z]+}}»>(«1,2,3 abc»); // Awal input dan sisanya. const auto [value, rest] = scan::scan_prefix<«{},{}»>(line).take (); // Satu catatan dalam satu waktu, dari apa saja. for (const row& one : scan::each<«{},{[az]+}\\n»>(text).of ()) { … }
Rentang yang hanya dapat dibaca sekali dibaca tanpa buffering, dengan field dikumpulkan saat karakter tiba. Jumlah karakter yang akan disimpan ditentukan oleh pola pada waktu kompilasi; jika jumlah ini tidak tersedia, pembacaan ditolak pada waktu kompilasi. 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 (sumber)) .dari ()) { … } Performa.
Pengukuran dilakukan pada Ryzen 9 9950X, clang 22.1.8 dengan libc++, -O3 -march=native,LTO. 32 penulisan per lintasan, median tujuh lintasan. "([az]+),([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 lima bidang yang sama, masing-masing dua ratus huruf scan::scan .sentinel() 57.8 ns re2c 637 ns CTRE 692 ns RE2 11751 ns Pengenalan "first.last@subdomain.example.com", tidak ada yang ditemukan scan::match .sentinel().scalar() 436 ns scan::match .scalar() 545 ns re2c 1186 ns RE2 2616 ns CTRE 14339 ns Menggabungkan kelompok-kelompok saat dianalisis.
Tipe tersebut diberi tahu ke grup mana karakter berikutnya termasuk, dan evaluasi dilakukan di tempat: tidak ada iterasi loop yang disimpan, tidak ada substring yang dibuat. 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 letter) { one.marks += letter == 'Y' ? 2u: 1u; } static constexpr tally finish_groups(state_type one) { return {one.total}; } }; const reading got = scan::scan<“value={}{[az]*}”>(“value=(__X_XX)abcdefgh”).of (); // got.number.value == 12, got.tail == "abcdefgh"
Objek pemanggil apa pun—alokator, pool, arena, apa pun—dapat diteruskan ke panggilan yang membuat nilai tersebut. Tipe konteks tidak hilang: status pemindai dan setiap kaitnya dapat dibuat templatnya, sehingga tidak ada tipe pemanggil yang disebutkan dalam pemindai. struct arena { std::pmr::memory_resource* where = nullptr; std::pmr::memory_resource* resource() const { return where; } }; struct numbers { std::pmr::vector nilai; }; struct keduanya { angka kiri; angka kanan; }; template <> struct scan::scanner { static constexpr std::string_view pattern() { return "([0-9]+)(?:,([0-9]+))*"; } template struct state { std::pmr::vector nilai; int berjalan = 0; }; keadaan statis begin_groups() { return {}; } template membutuhkan membutuhkan (const Told& one) { one.resource(); } status statis begin_groups(const Told& told) { return {std::pmr::vector (diberitahu.sumber daya()), 0}; } templat statis void push_group(state) & satu, scan::group_at , char digit) { one.running = one.running * 10 + (digit - '0'); } template statis void closed_group(state) & satu, scan::group_at ) { one.values.push_back(one.running); one.running = 0; } template angka statis finish_groups(state satu) { kembalikan {std::move(one.values)}; } }; std::pmr::monotonic_buffer_resource bytes; const arena mine{&bytes}; scan::scan<“{} {}”>(text).of (milikku); // satu untuk kedua tempat scan::scan<«{} {}»>(teks).of (milikku, scan::default_context); // satu per tempat scan::scan<“{} {}”>(text).of ({mine, scan::default_context}); // sama di dalam kurung const both got = scan::scan<«{} {}»>(text).with(mine); // Konteks tidak harus berupa tipenya: alokator sampai pada // konstruksi baca. 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 tersebut menerima objek pemanggil itu sendiri, bukan salinannya, sehingga status dapat menyimpan alamatnya: konteksnya tetap ada selama panggilan berlangsung, dan semua pembacaan terjadi di dalam panggilan tersebut. Fitur lainnya
- Dua lapisan: lapisan templat (match, starts_with, search, search_all, split) dan lapisan format, di mana "{}" adalah sebuah field, dan nilai tempatnya ditentukan oleh field dari tipe itu sendiri.
- Aturan disambiguasi adalah paling kiri dulu, seperti pada Perl, RE2, dan CTRE.
- Semua hal di atas juga berlaku pada ekspresi konstan.
- Tidak ada lookaround, backreference, atau properti Unicode; template beroperasi pada byte.
- Pola yang hanya diketahui saat runtime tidak didukung.
Majelis
Dikompilasi dengan clang dan GCC. Modul C++ bersifat opsional: file header (include/) yang dihasilkan dari modul yang sama oleh utilitas demodulizer tersedia—setiap push ke main akan membangun ulang, menautkan, dan melakukan commit. Satu-satunya dependensi, Boost.PFR, tidak diperlukan ketika paket binding C++26 diaktifkan. 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)
Sumber: opennet.ru
