Slicing and Splitting Strings
A parser often needs only one field from a record, while a command line or delimited row needs to be divided into many parts. In both cases, copying every selected character would be needless work when the source text can safely remain alive.
Slices and split results are ordinary read-only String values that can share the
source backing store.
This page shows how to describe their boundaries correctly, when byte coordinates are the efficient choice, and what it
means for the lifetime of a small result that still refers to a much larger source.
Describe a Boundary in the Right Coordinate System
Use byte coordinates for UTF-8 positions returned by search, parsing, or iteration APIs. They address the native storage directly and avoid rescanning text to translate a code-point position.
Use code-point coordinates when the position is naturally counted in decoded characters, such as a short fixed-format identifier. For UTF-8 and UTF-16, converting a code-point index can require scanning from a known boundary.
/// Byte-range slicing is the fast path for cutting `String` data into
/// smaller views.
///
/// Search operations such as `find()` return byte indexes. You can use these
/// indexes directly to build `ByteRange` values and pass them to `slice()`.
/// The resulting read-only strings refer to the same backing text and do not copy
/// the selected bytes.
void byteRangeSlicing() {
const auto journal = el::String{"dag=12|plats=Norrpasset|väder=klar|signal=stjärna"_el};
// Find separator positions once, then slice the fields between them.
const auto firstSeparator = journal.find("|"_el);
auto placeStart = firstSeparator;
journal.advance(placeStart);
const auto secondSeparator = journal.find("|"_el, placeStart);
auto weatherStart = secondSeparator;
journal.advance(weatherStart);
const auto thirdSeparator = journal.find("|"_el, weatherStart);
const auto day = journal.slice(el::ByteRange{el::ByteIndex::zero(), firstSeparator});
const auto place = journal.slice(el::ByteRange{placeStart, secondSeparator});
const auto weather = journal.slice(el::ByteRange{weatherStart, thirdSeparator});
el::io::printLine("Journal: "_el, journal);
el::io::printLine("First separator at byte: "_el, firstSeparator);
el::io::printLine("Day: "_el, day);
el::io::printLine("Place: "_el, place);
el::io::printLine("Weather: "_el, weather);
}
Journal: dag=12|plats=Norrpasset|väder=klar|signal=stjärna
First separator at byte: 6
Day: dag=12
Place: plats=Norrpasset
Weather: väder=klar
/// Code-point range slicing is useful for short, fixed-shape text where the
/// positions are naturally counted in decoded characters.
///
/// For UTF-8 and UTF-16 strings, a code-point range requires scanning from the
/// beginning of the text to find the matching storage positions. Use it for
/// small identifiers and labels, not as an inner-loop strategy for large
/// documents.
void codePointRangeSlicing() {
const auto label = el::String{"Färd-Karta-07"_el};
// The label is short and fixed-shape, so code-point positions are readable.
const auto mapName = label.slice(el::CpRange{el::CpIndex{5U}, el::CpLength{5U}});
const auto number = label.slice(el::CpRange{el::CpIndex{11U}, el::CpLength{2U}});
el::io::printLine("Label: "_el, label);
el::io::printLine("Map name: "_el, mapName);
el::io::printLine("Map number: "_el, number);
el::io::printLine("Map name byte length: "_el, mapName.length());
}
Label: Färd-Karta-07
Map name: Karta
Map number: 07
Map name byte length: 5
Keep the Beginning, End, or Middle
Use slice(), first(), and last() when the result should remain a shared read-only value.
The slice retains ownership of the backing store, so it remains valid independently of the source object.
/// Side-based slicing takes a prefix or suffix without spelling out a full
/// range.
///
/// Use `slice(StringSide::Front, ByteLength)` when a previous byte-index search
/// already told you how long the prefix is. Use
/// `slice(StringSide::Back, CpLength)` when the suffix is naturally counted in
/// decoded characters. The byte length of that suffix can then be used to take
/// the remaining prefix efficiently.
void frontBackSlicing() {
const auto route = el::String{"Rutt: Åsleden -> Nordljus"_el};
// A byte index from `find()` can become the prefix byte length.
const auto separator = route.find(" -> "_el);
const auto origin = route.slice(el::StringSide::Front, separator.distanceFromZero());
// A destination name is user-visible text, so take it as code points.
const auto destination = route.slice(el::StringSide::Back, el::CpLength{8U});
const auto withoutDestination = route.slice(el::StringSide::Front, route.length() - destination.length());
el::io::printLine("Route: "_el, route);
el::io::printLine("Origin: "_el, origin);
el::io::printLine("Destination: "_el, destination);
el::io::printLine("Without destination: ["_el, withoutDestination, "]"_el);
}
Route: Rutt: Åsleden -> Nordljus
Origin: Rutt: Åsleden
Destination: Nordljus
Without destination: [Rutt: Åsleden -> ]