Rilis pertama dari scan, sebuah pustaka penguraian teks untuk C++ dengan konstruksi mesin keadaan pada waktu kompilasi.

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"

pemindaian tumpukan::pindai .scalar() pemindaian tulisan tangan::pemindaian 10 141 ns 41.4 ns 42.5 ns 170 ns 100 439 ns 341 ns 392 ns 1176 ns 1000 3314 ns 3170 ns 3797 ns 11887 ns
Konteks dan alokator

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

Beli hosting yang andal untuk situs dengan perlindungan DDoS, server VPS VDS 🔥 Beli hosting website andal dengan perlindungan DDoS, server VPS VDS | ProHoster