Scanin ensimmäinen julkaisu, C++:n tekstinjäsennyskirjasto, jossa tilakone rakennetaan käännösaikana.

Skannauskirjaston ensimmäinen versio (0.1.0) on julkaistu. Se jäsentää tekstin arvoiksi käännösaikaisen mallin perusteella. Malli tai muoto kirjoitetaan C++-templaattiargumenttina ja käännetään tägätyksi deterministiseksi äärelliseksi automaatiksi (TDFA), jonka läpikulku avataan tila kerrallaan käännösaikaiseksi koodiksi C++-templaattien avulla. Suorituksen tulos on pyydetyn tyyppinen arvo. Projektikoodi on kirjoitettu C++23:lla ja se jaetaan GPLv3-lisenssillä. import scan; // Täsmääkö koko merkkijono? Jokainen aloituspiste on myös aluesovitin. scan::match<«[az]+@[az.]+»>(address); address | scan::match<«[az]+@[az.]+»>; // Ryhmillä. const auto found = scan::match<«([0-9]+)-([az]+)»>(«42-abc»); found.get<1>().to_view(); // "42" // Hahmon käyttämän merkkijonon alku ja ensimmäinen osuma missä tahansa. scan::starts_with<«[a-z]+»>(«abc123»).whole().to_view(); // "abc" scan::search<«[0-9]+»>(«id=4210x»).to_view(); // "4210" // Kaikki osumat ja niiden väliset osat ovat laiskoja näkymiä. for (const auto& one : text | scan::search_all<«[a-z]+»>) { … } const auto fields = "a,bb,,ccc" | scan::split<«,»> | std::ranges::to (); // Arvoja, ei tekstiä. struct row { int id; std::string_view name; }; const row one = scan::scan<«{},{[a-z]+}»>(line); // Lista, tyyppien summa, sisäkkäinen muoto. struct all { std::vector arvot; std::variantti tail; }; const all got = scan::scan<«{{}{*,?}} {{[0-9]+}|{[a-z]+}}»>(«1,2,3 abc»); // Syötteen alku ja siitä jäljellä oleva osa. const auto [value, rest] = scan::scan_prefix<«{},{}»>(line).take (); // Yksi tietue kerrallaan, mistä tahansa. for (const row& one : scan::each<«{},{[a-z]+}\\n»>(text).of ()) { … }

Kerran luettavissa oleva alue luetaan ilman puskurointia, ja kentät kerätään merkkien saapuessa. Säilytettävien merkkien lukumäärä määritetään mallin mukaan käännösaikana; jos tätä lukumäärää ei ole saatavilla, lukeminen hylätään käännösaikana. 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 (lähde)) ./ ()) { … } Suorituskykyä.

Mittaukset tehtiin Ryzen 9 9950X -prosessorilla, clang 22.1.8 ja libc++, -O3 -march=native,LTO. 2 kirjoituskertaa per läpimeno, keskimäärin seitsemän läpimenokertaa. "([az]+),([az]+),([az]+),([az]+),([az]+)" "alpha,bravo,charlie,delta,echo" scan::scan .sentinel() 477 ns uudelleentarkastelu 554 ns scan::scan 659 ns CTRE 717 ns RE2 15986 ns samat viisi kenttää, kaksisataa kirjainta kussakin scan::scan .sentinel() 57.8 ns re2c 637 ns CTRE 692 ns RE2 11751 ns "first.last@aliverkkotunnus.esimerkki.com" tunnistus, ei mitään noudettu scan::match .sentinel().scalar() 436 ns scan::match .scalar() 545 ns re2c 1186 ns RE2 2616 ns CTRE 14339 ns Ryhmien kutistaminen analysoitaessa

Tyypille kerrotaan, mihin sen ryhmistä seuraava merkki kuuluu, ja arviointi suoritetaan paikan päällä: silmukan iteraatioita ei tallenneta eikä alimerkkijonoja luoda. 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 += kirjain == '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 (); // sai.luku.arvo == 12, sai.häntä == "abcdefgh"

keon skannaus::skannaus .scalar() käsin kirjoitettu scan::scan 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
Kontekstit ja allokaattorit

Mikä tahansa kutsujaobjekti – allokaattori, pooli, areena tai mikä tahansa – voidaan välittää arvon luovalle kutsulle. Kontekstin tyyppiä ei menetetä: skannerin tila ja jokainen sen hookseista voidaan mallintaa siihen, joten skannerissa ei mainita kutsujatyyppiä. struct arena { std::pmr::memory_resource* where = nullptr; std::pmr::memory_resource* resource() const { return where; } }; struct numbers { std::pmr::vector arvot; }; struct molemmat { numerot vasemmalla; numerot oikealla; }; template <> struct scan::scanner { static constexpr std::string_view pattern() { return "([0-9]+)(?:,([0-9]+))*"; } malline rakennetila { std::pmr::vector arvot; int käynnissä = 0; }; staattinen tila begin_groups() { return {}; } malline vaatii vaatii(const Told& one) { yksi.resurssi(); } staattisessa tilassa begin_groups(const Told& told) { return {std::pmr::vector} (told.resource()), 0}; } malline static void push_group(tila & yksi, skannaus::group_at , char numero) { one.running = one.running * 10 + (numero - '0'); } malline static void closed_group(tila & yksi, skannaus::group_at ) { one.values.push_back(one.running); one.running = 0; } malline staattiset luvut finish_groups(tila one) { return {std::move(one.values)}; } }; std::pmr::monotonic_buffer_resource bytes; const arena mine{&bytes}; scan::scan<“{} {}”>(text).of (mine); // yksi molemmille paikoille scan::scan<«{} {}»>(text).of (mine, scan::default_context); // yksi paikkaa kohden scan::scan<“{} {}”>(text).of ({mine, scan::default_context}); // sama suluissa const both got = scan::scan<«{} {}»>(text).with(mine); // Kontekstin ei tarvitse olla tyyppiään: allokaattori menee niin pitkälle kuin // konstruoi luetun. struct two { std::pmr::string name; std::pmr::string tail; }; const two kept = scan::scan<«{[az]+} {[az]+}»>(text).of<( std::pmr::polymorphic_allocator<>(&bytes));

Koukku vastaanottaa kutsujan objektin itse, ei sen kopiota, joten tila voi tallentaa sen osoitteen: konteksti on olemassa yhtä kauan kuin kutsu, ja kaikki lukeminen tapahtuu kyseisen kutsun aikana. Muut ominaisuudet

  • Kaksi tasoa: mallitaso (match, starts_with, search, search_all, split) ja muotoilutaso, jossa "{}" on kenttä ja paikkamerkintä määräytyy itse tyypin kenttien perusteella.
  • Täsmennyssääntö on vasemmanpuoleisin ensin, kuten Perlissä, RE2:ssa ja CTRE:ssä.
  • Kaikki yllä oleva toimii myös vakiolausekkeissa.
  • Siinä ei ole ympäriottoja, takaviittauksia tai Unicode-ominaisuuksia; mallit toimivat tavujen perusteella.
  • Vain suorituksen aikana tunnettuja malleja ei tueta.

kokoonpano

Kääntyy clangin ja GCC:n avulla. C++-moduulit ovat valinnaisia: demodulointityökalun samoista moduuleista luomat otsikkotiedostot (include/) ovat käytettävissä – jokainen push-tiedosto pääuudelleenrakennuksiin, linkittää ne ja tekee commit-tiedoston. Ainoa riippuvuus, Boost.PFR, ei ole tarpeen, kun C++26-sidontapaketit ovat käytössä. 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)

Lähde: opennet.ru

Osta luotettava isännöinti sivustoille, joissa on DDoS-suojaus, VPS VDS -palvelimet 🔥 Osta luotettavaa verkkosivustojen hostingia DDoS-suojauksella, VPS VDS -palvelimilla | ProHoster