This is a C++-only convenience layer; the C ABI provides the underlying scans sz_find_best and sz_find_byteset_best that you compose by hand.
partition and rpartition return a three-way sz::string_partition_result struct with before, match, and after members around the first or last occurrence of a pattern.
The pattern can be a view, a single character, or a byte-set.
split, rsplit, and splitlines return lazy ranges of string_views. split(view) yields a sz::find_splits_view, rsplit(view) yields a sz::rfind_splits_view, and the byte-set overloads split(byteset) and rsplit(byteset) yield the matching character-splitter views. split(byteset) and rsplit(byteset) default to whitespace, and splitlines() defaults to the newline set.
A trailing separator yields a final empty segment, and an empty separator never splits, yielding the whole text as the only segment.
The key property is that these ranges are lazy — they borrow from the source buffer and walk it one string_view at a time, so the next boundary is computed only when you ask for it.
Splitting a large document into millions of tokens therefore costs no per-token allocation: each iteration step advances a cursor and hands back a borrowed view.
This is the decisive contrast with the idiomatic STL approach, where splitting builds a std::vector<std::string> and heap-allocates a fresh string for every element, copying its bytes out of the source.
#include <stringzilla/stringzilla.hpp>namespace sz = ashvardanian::stringzilla;int main() { sz::string_view_t csv = "alice,30,engineer"; sz::string_partition_result<sz::string_view_t> first = csv.partition(','); sz::string_view_t name = first.before; assert(name == "alice"); assert(first.match == ","); assert(first.after == "30,engineer"); std::size_t fields = 0; for (sz::string_view_t field : csv.split(",")) // "alice", "30", "engineer" ++fields; // no string is allocated per field assert(fields == 3); sz::string_view_t doc = "line one\nline two\nline three"; for (sz::string_view_t line : doc.splitlines()) (void)line; return 0;}
Each range exposes begin/end, dereferences to a sz::string_view_t, and can be consumed in a single pass.
When you genuinely need the tokens to outlive the source you can still materialize them into a std::vector<sz::string_view_t>, but only then do you pay for storage — the default path stays allocation-free.
API
Types
find_splits_viewA range of string slices for different splits of the data.
template <typename string_type_,
typename matcher_type_>
class find_splits_view
In some sense, represents the inverse operation to find_matches_view, as it reports not the search matches but the data between them. Meaning that for N search matches, there will be N+1 elements in the range, the last one empty when the text ends with a separator. Unlike ::find_matches_view, this range can't be empty, and an empty separator yields the whole text as its only segment. It also can't report overlapping intervals.
Notes
Lifetime semantics: Stores forwarded objects, owning strings included, to keep them alive. Iterators receive lightweight views only, so iteration is safe from ownership bugs.
For-loop optimized: Iterators are lightweight views with minimal register pressure, ideal for high-performance applications where cache efficiency and register allocation matter.
Sentinel support: Supports sentinel-based iteration via operator==(end_sentinel_t) for efficient termination without constructing full end iterators.
Compatible with C++20 ranges, std::string_view, and of course, StringZilla.
Similar to a pair of boost::algorithm::split_iterator.
rfind_splits_viewA range of string slices for different splits of the data in reverse-order.
template <typename string_type_,
typename matcher_type_>
class rfind_splits_view
In some sense, represents the inverse operation to find_matches_view, as it reports not the search matches but the data between them. Meaning that for N search matches, there will be N+1 elements in the range, the last one empty when the text starts with a separator. Unlike ::find_matches_view, this range can't be empty, and an empty separator yields the whole text as its only segment. It also can't report overlapping intervals.
Notes
Lifetime semantics: Stores forwarded objects, owning strings included, to keep them alive. Iterators receive lightweight views only, so iteration is safe from ownership bugs.
For-loop optimized: Iterators are lightweight views with minimal register pressure, ideal for high-performance applications where cache efficiency and register allocation matter.
Sentinel support: Supports sentinel-based iteration via operator==(end_sentinel_t) for efficient termination without constructing full end iterators.
Compatible with C++20 ranges, std::string_view, and of course, StringZilla.
Similar to a pair of boost::algorithm::split_iterator.
split_parts_tWhich parts of the split a utf8_split_view yields at each separator the kernel reports.
The shared engine behind the between-segment views utf8_split_newlines_view, utf8_split_whitespaces_view, and utf8_split_delimiters_view, and the delimiter-run views utf8_newlines_view, utf8_whitespaces_view, and utf8_delimiters_view. The kernel reports the byte spans of the delimiters, and parts_ selects what the view yields:
between_k: the N+1 segments between delimiters, the friendly lines, tokens, or fields;
separators_k: the N delimiter runs themselves, the newlines, whitespace, or delimiters;
both_k: the 2N+1 items interleaved, so concatenating them reconstructs the input exactly.
Each batch's separator endpoints are expanded once into a single boundary array, so every span is just bounds_[i] .. bounds_[i+1] and parts_ reduces to a compile-time (first,stride) over it: the iterator's hot path is one branchless formula shared by all three modes. empties_, a compile-time switch reachable via .skip_empty(), drops empty segments, and both_k is lossless only when empties are kept.