C++ Strings: Complete std::string Guide with Examples
C++ Strings Overview
C++ offers two string systems: C-style character arrays (null-terminated char[]) and std::string from the <string> header. In modern C++, std::string is the default choice. It manages its own memory, knows its own length, supports comparison operators, and integrates with the standard library.
C-strings still appear in legacy code, C APIs, and performance-critical paths. But for day-to-day programming, std::string is safer, more expressive, and almost as fast — especially since move semantics eliminated most copy overhead.
Creating Strings
#include <iostream>
#include <string>
using namespace std;
int main() {
// From string literal
string s1 = "Hello, World!";
string s2("Hello, World!");
string s3{"Hello, World!"};
// Empty string
string empty;
cout << "Empty length: " << empty.length() << endl; // 0
// Repeat a character
string dashes(40, '-'); // 40 dashes
cout << dashes << endl;
// From C-string
const char* cstr = "C-string";
string s4(cstr);
// Substring of another string
string s5(s1, 0, 5); // "Hello"
string s6(s1, 7); // "World!"
// From iterators
string s7(s1.begin(), s1.begin() + 5); // "Hello"
cout << s5 << " | " << s6 << " | " << s7 << endl;
return 0;
}
Accessing Characters
#include <iostream>
#include <string>
using namespace std;
int main() {
string text = "Hello";
// operator[] — no bounds checking
cout << text[0] << endl; // H
cout << text[4] << endl; // o
// .at() — throws on out-of-bounds
cout << text.at(1) << endl; // e
try { text.at(10); }
catch (const out_of_range& e) { cout << "Error: " << e.what() << endl; }
// front() and back()
cout << "First: " << text.front() << endl; // H
cout << "Last: " << text.back() << endl; // o
// Modify characters
text[0] = 'J';
text.back() = '!';
cout << text << endl; // Jell!
// C-string access
const char* raw = text.c_str(); // Null-terminated
const char* data = text.data(); // Same in C++11+
cout << raw << endl;
return 0;
}
Modifying Strings
#include <iostream>
#include <string>
using namespace std;
int main() {
string s = "Hello World";
// append
s.append("!!!");
cout << s << endl; // Hello World!!!
// insert
s.insert(5, ",");
cout << s << endl; // Hello, World!!!
// erase
s.erase(12, 3); // Remove 3 chars starting at index 12
cout << s << endl; // Hello, World
// replace
s.replace(7, 5, "C++");
cout << s << endl; // Hello, C++
// push_back / pop_back
s.push_back('!');
cout << s << endl; // Hello, C++!
s.pop_back();
cout << s << endl; // Hello, C++
// resize
s.resize(5);
cout << s << endl; // Hello
// clear
s.clear();
cout << "Empty: " << s.empty() << endl; // 1
return 0;
}
Concatenation
#include <iostream>
#include <string>
using namespace std;
int main() {
string first = "Hello";
string last = "World";
// operator+
string full = first + ", " + last + "!";
cout << full << endl;
// operator+=
string s = "Count: ";
s += "1, 2, 3";
cout << s << endl;
// append() — same as +=
string t = "A";
t.append("BC").append("DE"); // Chainable
cout << t << endl; // ABCDE
// Concatenating with numbers — use to_string()
int age = 25;
string msg = "Age: " + to_string(age);
cout << msg << endl;
// Using string literals operator (C++14)
using namespace string_literals;
auto greeting = "Hello "s + "World"s; // Both are std::string
cout << greeting << endl;
return 0;
}
Comparing Strings
#include <iostream>
#include <string>
using namespace std;
int main() {
string a = "apple";
string b = "banana";
string c = "apple";
// Comparison operators (lexicographic)
cout << (a == c) << endl; // 1 (true)
cout << (a != b) << endl; // 1 (true)
cout << (a < b) << endl; // 1 (a comes before b)
cout << (b > a) << endl; // 1
// .compare() — returns 0, negative, or positive
cout << a.compare(c) << endl; // 0 (equal)
cout << a.compare(b) << endl; // negative (a < b)
// Case-insensitive comparison (no built-in — DIY)
string x = "Hello", y = "hello";
auto to_lower = [](string s) {
for (char& c : s) c = tolower(c);
return s;
};
cout << "Case-insensitive equal: "
<< (to_lower(x) == to_lower(y)) << endl; // 1
return 0;
}
Searching and Finding
#include <iostream>
#include <string>
using namespace std;
int main() {
string text = "The quick brown fox jumps over the lazy dog";
// find — returns index or string::npos
size_t pos = text.find("fox");
if (pos != string::npos) {
cout << "fox at index " << pos << endl; // 16
}
// rfind — search from the end
cout << "Last 'the': " << text.rfind("the") << endl; // 31
// find_first_of — any character in the set
cout << "First vowel: " << text.find_first_of("aeiou") << endl;
// find_last_of
cout << "Last vowel: " << text.find_last_of("aeiou") << endl;
// find_first_not_of
string padded = " hello ";
size_t start = padded.find_first_not_of(' ');
size_t end = padded.find_last_not_of(' ');
cout << "Trimmed: [" << padded.substr(start, end - start + 1) << "]" << endl;
// Count occurrences
int count = 0;
size_t p = 0;
while ((p = text.find("the", p)) != string::npos) {
count++;
p += 3;
}
cout << "'the' appears " << count << " times" << endl;
return 0;
}
Substrings and Slicing
#include <iostream>
#include <string>
using namespace std;
int main() {
string s = "Hello, World!";
// substr(start, length)
string word1 = s.substr(0, 5); // "Hello"
string word2 = s.substr(7); // "World!"
string word3 = s.substr(7, 5); // "World"
cout << word1 << " | " << word2 << " | " << word3 << endl;
// Split by delimiter
string csv = "apple,banana,cherry,date";
size_t start = 0, end;
while ((end = csv.find(',', start)) != string::npos) {
cout << csv.substr(start, end - start) << endl;
start = end + 1;
}
cout << csv.substr(start) << endl; // Last token
return 0;
}
Number-String Conversions
#include <iostream>
#include <string>
using namespace std;
int main() {
// Number to string
string s1 = to_string(42);
string s2 = to_string(3.14159);
string s3 = to_string(-100);
cout << s1 << " | " << s2 << " | " << s3 << endl;
// String to number
int n1 = stoi("42");
long n2 = stol("1000000");
double n3 = stod("3.14");
float n4 = stof("2.718");
cout << n1 << " | " << n2 << " | " << n3 << " | " << n4 << endl;
// stoi with base
int hex = stoi("FF", nullptr, 16); // 255
int bin = stoi("1010", nullptr, 2); // 10
cout << "Hex FF = " << hex << endl;
cout << "Bin 1010 = " << bin << endl;
// Error handling
try {
int bad = stoi("not_a_number");
} catch (const invalid_argument& e) {
cout << "Invalid: " << e.what() << endl;
}
return 0;
}
Iterating Over Strings
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main() {
string text = "Hello World";
// Range-for
for (char c : text) cout << c << " ";
cout << endl;
// Modify with reference
for (char& c : text) c = toupper(c);
cout << text << endl; // HELLO WORLD
// Algorithm: transform to lowercase
transform(text.begin(), text.end(), text.begin(), ::tolower);
cout << text << endl; // hello world
// Reverse
reverse(text.begin(), text.end());
cout << text << endl; // dlrow olleh
// Count specific character
int spaces = count(text.begin(), text.end(), ' ');
cout << "Spaces: " << spaces << endl;
return 0;
}
String Streams
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
int main() {
// Building strings with ostringstream
ostringstream oss;
oss << "Name: " << "Alice" << ", Age: " << 25 << ", GPA: " << 3.95;
string formatted = oss.str();
cout << formatted << endl;
// Parsing with istringstream
string data = "10 20 30 40 50";
istringstream iss(data);
vector<int> nums;
int n;
while (iss >> n) {
nums.push_back(n);
}
for (int x : nums) cout << x << " ";
cout << endl;
// Split by comma using getline with delimiter
string csv = "apple,banana,cherry";
istringstream css(csv);
string token;
while (getline(css, token, ',')) {
cout << "[" << token << "] ";
}
cout << endl;
return 0;
}
std::string_view (C++17)
std::string_view is a lightweight, non-owning reference to a string. It avoids copying when you just need to read a portion of a string.
#include <iostream>
#include <string>
#include <string_view>
using namespace std;
// Accepts string, C-string, or string_view — no copying!
void print_name(string_view name) {
cout << "Hello, " << name << "!" << endl;
}
int main() {
string s = "Alice";
const char* c = "Bob";
print_name(s); // From std::string
print_name(c); // From C-string
print_name("Charlie"); // From literal
// string_view operations (no allocation)
string_view sv = "Hello, World!";
cout << sv.substr(0, 5) << endl; // Hello (returns string_view)
cout << sv.starts_with("Hello") << endl; // 1 (C++20)
// WARNING: string_view does NOT own the data
// Don't return a string_view to a local string!
return 0;
}
Raw String Literals
#include <iostream>
#include <string>
using namespace std;
int main() {
// Regular string — must escape backslashes and quotes
string path = "C:\\Users\\Documents\\file.txt";
string json = "{\"name\": \"Alice\"}";
// Raw string literal R"(...)" — no escaping needed
string raw_path = R"(C:\Users\Documents\file.txt)";
string raw_json = R"({"name": "Alice", "age": 25})";
cout << raw_path << endl;
cout << raw_json << endl;
// Multi-line raw strings
string html = R"(
<html>
<body>
<h1>Hello</h1>
</body>
</html>
)";
cout << html << endl;
// Custom delimiter for strings containing ")
string tricky = R"delim(She said "Hello" and typed R"(raw)")delim";
cout << tricky << endl;
return 0;
}
Common Mistakes
1. Comparing C-strings with ==:
const char* a = "hello";
const char* b = "hello";
// if (a == b) — compares POINTERS, not content!
// Fix: use strcmp(a, b) == 0, or use std::string
2. Returning string_view to local:
string_view bad() {
string local = "temporary";
return local; // DANGLING — local destroyed after return
}
3. Forgetting that + doesn’t work between two C-strings:
// auto s = "Hello" + " World"; // ERROR: can't add two char arrays
auto s = "Hello"s + " World"; // Fix: make one a std::string
Practice Exercises
Exercise 1: Write a function that checks if a string is a palindrome, ignoring case and non-alphanumeric characters.
Exercise 2: Write a function that counts the number of words in a string.
Exercise 3: Implement a simple find-and-replace function that replaces all occurrences of a substring.
Exercise 4: Write a CSV parser that reads a comma-separated string and returns a vector<vector<string>>.
Summary
std::string is a powerful, safe, and efficient string type that handles memory automatically. You learned creation, access, modification, searching, substrings, number conversions, string streams, and the modern string_view for zero-copy reads. Use std::string by default, string_view for read-only parameters, and C-strings only when interfacing with C APIs. In the next lesson, you will learn structures — your first step toward custom data types.