Pitfalls of absl::StrSplit with Empty Strings and Edge Cases
absl::StrSplit returns different results for null versus empty string_view objects, falls back to character-wise splitting when given empty delimiters, and silently truncates excess elements when targeting fixed-size containers.
The absl::StrSplit function in the Abseil C++ library provides a flexible, template-based API for tokenizing strings, but its generic design creates subtle pitfalls when handling empty inputs, null string views, and boundary conditions. Understanding these edge cases requires examining the implementation details in absl/strings/str_split.h and the comprehensive test coverage in absl/strings/str_split_test.cc.
The Null string_view Legacy Bug
One of the most confusing edge cases involves the distinction between an empty string and a default-constructed absl::string_view. While both represent zero-length data, they produce different results:
absl::StrSplit(absl::string_view(""), '-'); // Returns: {""}
absl::StrSplit(absl::string_view(), '-'); // Returns: {} (legacy bug)
The first call creates a non-null string_view with zero length, while the second creates a null string_view. According to the source code in absl/strings/internal/str_split_internal.h and tests at lines 28-33 of absl/strings/str_split_test.cc, the implementation contains a historic compatibility shim that returns an empty collection for null views.
Best Practice: Always pass a concrete empty string ("" or std::string()) rather than a default-constructed string_view to ensure consistent behavior across future library versions.
Empty Delimiter Falls Back to Character Splitting
When the delimiter is an empty string, StrSplit deliberately falls back to character-wise splitting to avoid infinite loops. This special-case logic lives in the ByString and ByAnyChar constructors (lines 97-103 of absl/strings/str_split.h):
// This splits every character, not by a zero-length delimiter
std::vector<std::string> result = absl::StrSplit("abc", "");
// Result: {"a", "b", "c"}
The test at lines 79-86 of str_split_test.cc confirms this behavior. Pitfall: Passing an uninitialized std::string or empty delimiter variable unintentionally splits every character, which is usually not the desired outcome.
Controlling Empty Result Inclusion
By default, StrSplit includes empty substrings in the results using the AllowEmpty predicate. To filter them out, you must explicitly use absl::SkipEmpty() or absl::SkipWhitespace():
// Keeps empty substrings (default behavior)
auto keep = absl::StrSplit(",a,,b,", ',');
// Result: {"", "a", "", "b", ""}
// Drops empty substrings
auto drop = absl::StrSplit(",a,,b,", ',', absl::SkipEmpty());
// Result: {"a", "b"}
As shown in tests at lines 81-88 of str_split_test.cc, forgetting to add the predicate when parsing CSV-like data often leads to unexpected empty strings in your collections.
Fixed-Size Container Truncation
When splitting into fixed-size containers such as std::pair or std::array, excess elements are silently discarded while missing elements default to empty strings. The tests at lines 103-126 of str_split_test.cc demonstrate this behavior:
std::pair<std::string, std::string> p = absl::StrSplit("a,b,c", ',');
// p == {"a", "b"} // "c" is silently ignored
Warning: If your input format varies in field count, using fixed-size targets will silently lose data without throwing exceptions.
Memory Safety with Large Inputs
For arbitrarily large strings (including those exceeding 2 GiB), StrSplit avoids copying by returning absl::string_view objects referencing the original buffer (confirmed by tests at lines 46-64 of str_split_test.cc).
Critical Safety Note: The original buffer must outlive the resulting views. Splitting a temporary std::string and storing the results for later use creates dangling references:
// DANGEROUS: Temporary string destroyed after expression
auto views = absl::StrSplit(GetTemporaryString(), ',');
// views now contains dangling string_views
Summary
- Null vs. Empty: Default-constructed
absl::string_viewreturns{}while empty string""returns{""}due to legacy compatibility code inabsl/strings/internal/str_split_internal.h. - Empty Delimiters: Unintentionally empty delimiters trigger character-wise splitting rather than errors, as implemented in
ByString(lines 97-103 ofstr_split.h). - Empty Results: Use
absl::SkipEmpty()to filter empty substrings; the defaultAllowEmptypreserves them. - Fixed Containers:
std::pairandstd::arraytruncate excess fields silently—validate field counts before splitting. - Lifetime Safety: Results are
string_views into the original buffer; ensure the source string outlives the split results.
Frequently Asked Questions
Why does absl::StrSplit return different results for empty strings and null string_views?
The implementation contains a legacy compatibility hack in absl/strings/internal/str_split_internal.h that treats default-constructed (null) string_view objects differently from empty-but-non-null views. A null view returns an empty collection {}, while an empty string returns a collection containing one empty string {""}. This behavior is tested at lines 28-33 of str_split_test.cc but may change in future versions.
What happens when I pass an empty string as the delimiter to absl::StrSplit?
Rather than causing an infinite loop or error, empty delimiters trigger a fallback to character-wise splitting. The ByString and ByAnyChar classes detect empty delimiters in their constructors (lines 97-103 of str_split.h) and split the input into individual characters. This is usually unintended unless you specifically need to iterate over characters.
How do I split a string and automatically remove empty results?
Pass absl::SkipEmpty() as the third argument to absl::StrSplit. By default, the function uses the AllowEmpty predicate which preserves empty substrings between consecutive delimiters or at the string boundaries. The SkipEmpty predicate filters these out during the split operation, as demonstrated in tests at lines 81-88 of str_split_test.cc.
Is it safe to split very large strings with absl::StrSplit?
Yes, but with lifetime considerations. The function returns absl::string_view objects pointing into the original buffer without copying data (verified by tests at lines 46-64 of str_split_test.cc). While this handles multi-gigabyte strings efficiently, you must ensure the original string remains alive as long as you access the split results to avoid use-after-free bugs.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →