Strings and Text
|
This section documents C++23 (ISO/IEC 14882:2024), as published by ISO/IEC JTC1/SC22/WG21 (wg21), verified against the freely available working draft N5046 (eel.is/c++draft) and cppreference.com. This content was generated with the assistance of AI and should be verified against the working draft and cppreference.com before being relied on in production. This section’s bibliography lists the reference material consulted while preparing these pages. |
std::string and std::string_view
std::string owns and manages a growable buffer of char; std::string_view (C++17) is a non-owning
(pointer, length) view — pass it by value to avoid copying when a function only needs to read text:
#include <string>
#include <string_view>
#include <iostream>
void printLength(std::string_view sv) { // accepts std::string, "literals", substrings -- no copy
std::cout << sv.size() << '\n';
}
int main() {
std::string s = "Hello, World!";
s += " Goodbye.";
printLength(s);
printLength("a literal"); // implicitly builds a string_view, not a std::string
printLength(std::string_view(s).substr(0, 5)); // "Hello" -- substr on a view is itself non-allocating
}
A string_view must never outlive the buffer it points into — a common bug is returning one that views a
now-destroyed temporary std::string.
Character Types and Unicode
C++ has no built-in Unicode-aware string type; std::string is a sequence of char (commonly UTF-8 encoded by
convention, not enforced by the type), std::u8string (C++20) is a sequence of char8_t explicitly meant for
UTF-8, and std::u16string/std::u32string hold UTF-16/UTF-32 code units:
#include <string>
std::string narrow = "caf\xc3\xa9"; // UTF-8 bytes for "café", typed as plain char
std::u8string utf8 = u8"café"; // same bytes, typed as char8_t -- distinguishable at compile time
std::u32string codepoints = U"café"; // one char32_t per Unicode code point (4 code points: c, a, f, é)
Iterating a UTF-8 std::string by char walks bytes, not code points or grapheme clusters — for real
Unicode segmentation, reach for a library like ICU or
a codepoint-aware view; the standard library alone does not provide
one.
Raw String Literals and User-Defined Literals
See Lexical Structure and Style for the general
raw-string and UDL syntax; the standard library’s own string UDLs live in std::literals::string_literals:
#include <string>
#include <string_view>
using namespace std::literals;
auto s = "hello"s; // std::string, not const char*
auto sv = "hello"sv; // std::string_view, zero-allocation
<charconv>: Locale-Independent Conversions
std::to_chars/std::from_chars (C++17) convert numbers to/from text without allocating, without exceptions,
and without any locale dependency — the fastest and most predictable conversion path available:
#include <charconv>
#include <array>
#include <string_view>
#include <cassert>
int main() {
std::array<char, 16> buffer{};
auto [ptr, ec] = std::to_chars(buffer.data(), buffer.data() + buffer.size(), 42);
assert(ec == std::errc{});
std::string_view written(buffer.data(), ptr - buffer.data());
assert(written == "42");
int parsed = 0;
auto [ptr2, ec2] = std::from_chars(written.data(), written.data() + written.size(), parsed);
assert(ec2 == std::errc{});
assert(parsed == 42);
(void)ptr2;
return 0;
}
std::format and std::print
std::format (C++20, header <format>) is a type-safe, Python-style formatting facility that replaces
`printf’s format-string/argument mismatches (a common source of undefined behavior) with compile-time-checked
placeholders:
#include <format>
#include <iostream>
#include <string>
int main() {
std::string s = std::format("{} is {} years old, pi ~= {:.2f}", "Ada", 36, 3.14159);
std::cout << s << '\n'; // Ada is 36 years old, pi ~= 3.14
std::cout << std::format("{0} {1} {0}", "a", "b") << '\n'; // positional args: a b a
std::cout << std::format("{:>10}|{:<10}|{:^10}", "r", "l", "c") << '\n'; // alignment
}
std::print/std::println (C++23, header <print>) write formatted text directly to a stream, skipping the
intermediate std::string:
#include <print>
int main() {
std::println("{} is {} years old", "Ada", 36);
}
<print> is standard-conformant C++23 but is not yet in this environment’s libstdc++ 13 — it needs GCC
14+, a recent libc++, or MSVC (see Getting Started);
std::format itself, used above, does compile locally.
|
Custom Formatters
A type opts into std::format by specializing std::formatter:
#include <format>
#include <string>
struct Point { int x, y; };
template <>
struct std::formatter<Point> : std::formatter<std::string> {
auto format(const Point& p, std::format_context& ctx) const {
return std::formatter<std::string>::format(
std::format("({}, {})", p.x, p.y), ctx);
}
};
// std::format("{}", Point{1, 2}) now yields "(1, 2)"
<regex>
#include <regex>
#include <string>
#include <iostream>
int main() {
std::string text = "order-42, order-7";
std::regex pattern(R"(order-(\d+))");
for (std::sregex_iterator it(text.begin(), text.end(), pattern), end; it != end; ++it) {
std::cout << "matched id: " << (*it)[1] << '\n'; // 42, then 7
}
}
<regex> is notorious for compile-time cost and, on some standard libraries, run-time performance well below
hand-written parsers or dedicated libraries (e.g. RE2, Boost.Xpressive) — fine for one-off scripts, worth
benchmarking before using on a hot path.
See Also
-
C: Strings and Text Processing —
chararrays and<string.h>, with the manual length and termination bookkeepingstd::stringremoves.