The std::string class is the workhorse of C++ text manipulation and part of the STL (Standard Library) and provides a high-level, convenient way to handle strings. It's defined in the <string> header.
You can declare and initialize std::string in several ways:
std::string str1; // Empty string
std::string str2 = "Hello"; // Initialization with a C-style string
std::string str3("World"); // Initialization using constructor
std::string str4 = str2; // Copy initialization
std::string str5(5, 'a'); // Initialize with 5 'a' characters ("aaaaa")You can concatenate strings using the + and += operators or the append() method:
str1 = str2 + " " + str3; // "Hello World"
str1 += "!";
str1.append(" How are you?");You can get the length of the string using length() or size():
size_t len = str1.length();You can also check the capacity and resize the string:
size_t cap = str1.capacity();
str1.resize(50);You can access individual characters using the [] operator or the at() method:
char firstChar = str1[0];
char secondChar = str1.at(1);To get a substring, you can use the substr() method:
std::string sub = str1.substr(0, 5); // Gets first 5 charactersTo find a substring or a character, you can use find():
size_t pos = str1.find("World"); // Returns position or std::string::npos if not foundYou can compare strings using relational operators (==, !=, <, >, etc.) or the compare() method:
if (str1 == str2) {
// Strings are equal
}To convert a std::string to a C-style string, you can use the c_str() method:
const char* cstr = str1.c_str();To convert numbers to strings or vice versa, you can use std::to_string() and std::stoi():
std::string numStr = std::to_string(42);
int num = std::stoi("42");Integer (std::stoi, std::stol, std::stoll): Convert to int, long, or long long.
int i = std::stoi("42");
long l = std::stol("42");
long long ll = std::stoll("42");Floating-Point (std::stof, std::stod, std::stold): Convert to float, double, or long double.
float f = std::stof("42.42");
double d = std::stod("42.42");
long double ld = std::stold("42.42");Unsigned Integer (std::stoul, std::stoull): Convert to unsigned long or unsigned long long.
unsigned long ul = std::stoul("42");
unsigned long long ull = std::stoull("42");You can iterate through a string using iterators or range-based for loops:
for (char c : str1) {
// Do something with c
}std::string manages memory automatically, resizing as needed.
When you append characters to a string and its size would exceed its capacity, the string must reallocate:
- A new, larger block of memory is allocated on the heap (often 1.5x or 2x the old capacity).
- All characters from the old block are copied to the new block.
- The old block of memory is deallocated.
When building a large string from many small pieces, call reserve() once if you have a reasonable estimate of the final size. This pre-allocates the necessary memory and avoids multiple, costly reallocations.
// Inefficient approach
std::string s;
for (int i = 0; i < 1000; ++i) {
s += get_next_piece(); // Potentially causes many reallocations
}
// Efficient approach
std::string s;
s.reserve(40000); // Estimate the final size and reserve once
for (int i = 0; i < 1000; ++i) {
s += get_next_piece(); // No reallocations will occur
}Unlike strings in some other languages, std::string objects are mutable. You can change them after they are created.
string(): Default constructorstring(const string& str): Copy constructorstring(const char* s): Construct from a C-stringstring(size_t n, char c): Construct by repeating a characterntimes
operator[]: Access elementat(): Access element, throws out_of_range exception for invalid indexfront(): Access the first characterback(): Access the last character
append(): Append to stringpush_back(): Append a character to the endinsert(): Insert into stringerase(): Erase a part of stringreplace(): Replace a part of stringswap(): Swap content with another stringpop_back(): Removes the last characterclear(): Clears the content
size() / length(): Return lengthempty(): Check if string is emptyresize(): Change sizereserve(): Reserve storagecapacity(): Get capacityshrink_to_fit(): Shrink to fit
c_str(): Get C string equivalentdata(): Get array of characterscopy(): Copy substring to C-arrayfind(): Find substringrfind(): Reverse findfind_first_of(): Find character from setfind_last_of(): Find last character from setfind_first_not_of(): Find character not in setfind_last_not_of(): Find last character not in setsubstr(): Generate a substringcompare(): Compare strings
operator+: Concatenate stringsoperator+=: Append to stringoperator== / operator!=: Compare equality/inequalityoperator< / operator<= / operator> / operator>=: Lexicographical compare
While std::stoi and its family are convenient, they have drawbacks for high-performance or robust applications: they can throw exceptions, perform memory allocations, and are dependent on the system's locale.
std::from_chars (from the <charconv> header) was introduced to solve these problems. It is a low-level, locale-independent, non-allocating, and non-throwing function designed for maximum performance.
It attempts to parse a number from a character range [first, last). It returns a struct, std::from_chars_result, which contains two essential members:
ptr: A pointer to the first character not consumed by the parse.ec: An error code of type std::errc. It equals std::errc() (no error) on success.
This mechanism provides fast, explicit, and fine-grained control over parsing.
Note
The following examples use std::string_view. Read more about that in the specialized page in this guide.
#include <charconv>
#include <string_view>
#include <iostream>
int main() {
// --- Integer Example ---
std::string_view sv_int = "12345";
int int_val = 0;
if (auto [p, ec] = std::from_chars(sv_int.begin(), sv_int.end(), int_val); ec == std::errc()) {
std::cout << "Parsed int: " << int_val << std::endl;
}
// --- Double Example ---
std::string_view sv_flt = "3.14159";
double dbl_val = 0.0;
if (auto [p, ec] = std::from_chars(sv_flt.begin(), sv_flt.end(), dbl_val); ec == std::errc()) {
std::cout << "Parsed double: " << dbl_val << std::endl;
}
}#include <charconv>
#include <string_view>
#include <iostream>
int main() {
std::string_view sv = "abc";
int value = 0;
auto [ptr, ec] = std::from_chars(sv.begin(), sv.end(), value);
if (ec == std::errc::invalid_argument) {
std::cout << "Failed to parse as expected: Not a valid number." << std::endl;
}
}The returned ptr is key for efficient parsing of multiple values from a single string without creating substrings.
#include <charconv>
#include <string_view>
#include <iostream>
int main() {
std::string_view sv = "42 198 -10";
int v1 = 0, v2 = 0, v3 = 0;
// Parse first number
auto res1 = std::from_chars(sv.begin(), sv.end(), v1);
std::cout << "Parsed: " << v1 << std::endl;
// Parse second, starting from where the first parse ended (res1.ptr)
auto res2 = std::from_chars(res1.ptr + 1, sv.end(), v2); // +1 to skip space
std::cout << "Parsed: " << v2 << std::endl;
// Parse third
auto res3 = std::from_chars(res2.ptr + 1, sv.end(), v3); // +1 to skip space
std::cout << "Parsed: " << v3 << std::endl;
}Output:
Parsed: 42
Parsed: 198
Parsed: -10
For floating-point numbers, you can enforce a specific format (fixed, scientific, or hex) for strict data validation. The default is general, which accepts both fixed and scientific.
#include <charconv>
#include <string_view>
#include <iostream>
int main() {
std::string_view sv = "1.23e4";
double value = 0;
// Attempt to parse with 'fixed' format -- this will fail.
auto res1 = std::from_chars(sv.begin(), sv.end(), value, std::chars_format::fixed);
if (res1.ec != std::errc()) {
std::cout << "'" << sv << "' is not a fixed-point number." << std::endl;
}
// Attempt to parse with 'scientific' format -- this will succeed.
auto res2 = std::from_chars(sv.begin(), sv.end(), value, std::chars_format::scientific);
if (res2.ec == std::errc()) {
std::cout << "Parsed '" << sv << "' as scientific: " << value << std::endl;
}
}Output:
'1.23e4' is not a fixed-point number.
Parsed '1.23e4' as scientific: 12300