Strings, string_view & format
Names, addresses, messages and file paths are all text. In C++ the main tool for text is std::string from the <string> header: a sequence of characters that grows and shrinks as you need. In this lesson you will build and search strings, pass text around cheaply with std::string_view, and produce neat, aligned output with std::format.
Creating and joining strings
#include <print>
#include <string>
int main() {
std::string first = "Ananya";
std::string last = "Iyer";
std::string full = first + " " + last; // + joins strings
std::println("{}", full);
std::println("Length: {}", full.size());
std::println("First letter: {}", full[0]);
std::println("Last letter: {}", full.back());
full += " (Pune)"; // append to the end
std::println("{}", full);
}Ananya Iyer Length: 11 First letter: A Last letter: r Ananya Iyer (Pune)
Each character has an index (position) starting from 0, so full[0] is 'A'. size() counts characters (strictly, bytes). The space counts too, which is why "Ananya Iyer" has 11. Square brackets do not check the index; full.at(50) does, and reports an error instead of reading random memory.
Searching and slicing
std::string has many useful member functions (functions you call with a dot). Here are the ones you will use most.
#include <print>
#include <string>
int main() {
std::string email = "rohan.verma@example.in";
auto at = email.find('@'); // position of '@'
std::string user = email.substr(0, at); // from 0, 'at' characters
std::string domain = email.substr(at + 1); // from at+1 to the end
std::println("@ is at index {}", at);
std::println("User: {}", user);
std::println("Domain: {}", domain);
std::println("Ends with .in? {}", email.ends_with(".in"));
std::println("Contains verma? {}", email.contains("verma"));
if (email.find('#') == std::string::npos) {
std::println("No # in the address");
}
}@ is at index 11 User: rohan.verma Domain: example.in Ends with .in? true Contains verma? true No # in the address
find returns the index of the first match, or the special value std::string::npos ("no position") when there is no match. substr(start, count) copies part of the string. starts_with and ends_with arrived in C++20 and contains in C++23; before that you had to write email.find("verma") != std::string::npos.
Strings also compare with ==, !=, < and >. The comparison is alphabetical by character code, so "apple" < "banana" is true, but every capital letter sorts before every small letter.
Converting between numbers and text
#include <print>
#include <string>
int main() {
std::string pin_text = "110001";
int pin = std::stoi(pin_text); // string to int
double fare = std::stod("845.50"); // string to double
std::string label = "Marks: " + std::to_string(87 + 5);
std::println("{} {}", pin + 1, fare * 2);
std::println("{}", label);
}110002 1691 Marks: 92
std::stoi and std::stod throw an exception if the text is not a number; you will learn to handle that in Exceptions, optional & expected. Notice that 1691.0 prints as 1691: the {} placeholder prints the shortest form that represents the value exactly. Use {:.2f} when you want fixed decimals.
string_view: looking without copying
Copying a long string takes time and memory. A std::string_view (header <string_view>, C++17) is a lightweight, read-only view of characters that live somewhere else. It is perfect for function parameters that only read text.
#include <print>
#include <string>
#include <string_view>
void greet(std::string_view name) {
std::println("Hello, {}! Your name has {} letters.", name, name.size());
}
int main() {
std::string student = "Farhan";
greet(student); // no copy is made
greet("Isha"); // works with plain text too
std::string_view city = "Lucknow, Uttar Pradesh";
std::println("{}", city.substr(0, 7));
}Hello, Farhan! Your name has 6 letters. Hello, Isha! Your name has 4 letters. Lucknow
city.substr(0, 7) on a view returns another view, so even slicing copies nothing.
Formatting with std::format
std::format (header <format>, C++20) builds a std::string using the same placeholder rules as std::println. Inside a placeholder, after a colon, you can set the width, the alignment (< left, > right, ^ centre), a fill character, and the precision.
#include <format>
#include <print>
#include <string>
int main() {
std::string header = std::format("{:<10}{:>8}", "Item", "Price");
std::println("{}", header);
std::println("{:<10}{:>8.2f}", "Pen", 25.0);
std::println("{:<10}{:>8.2f}", "Notebook", 120.5);
std::println("{:-^18}", " Total ");
std::println("{:<10}{:>8.2f}", "", 145.5);
std::println("Roll no: {:04}", 7);
std::println("Hex: {:x}, binary: {:b}", 255, 5);
}Item Price
Pen 25.00
Notebook 120.50
----- Total ------
145.50
Roll no: 0007
Hex: ff, binary: 101{:<10} pads to 10 characters with the text on the left, {:>8.2f} right-aligns a number with two decimals in 8 characters, and {:-^18} centres the text using - as the fill. {:04} pads a number with zeros. Use std::format when you need the text as a value, for example to save it in a file, and std::println when you just want to print it.
Common mistakes
- Comparing text with a char array or pointer. With
std::string,==compares the text. With two oldchar*strings it compares memory addresses, not text. - Forgetting that indexes start at 0. The last character is at
size() - 1. - Not checking
find. Usingnposas a position, as insubstr(npos), throws an error. Compare withstd::string::nposfirst. - Counting rupee signs and Hindi letters.
size()counts bytes, and in UTF-8 text a character such as the rupee sign uses 3 bytes, sosize()can be larger than the number of visible characters.
Key points
std::stringstores text; join with+, append with+=, and read characters with[]orat().find,substr,starts_with,ends_withand C++23'scontainscover most searching needs.std::stoi,std::stodandstd::to_stringconvert between text and numbers.std::string_viewis a cheap, read-only view, ideal for function parameters, but it must not outlive its text.std::formatandstd::printlnshare format specs such as{:<10},{:>8.2f}and{:04}.
Exercise
Given std::string full = "Meera Nair";, write a program that finds the space, extracts the first name and the surname with substr, and prints Surname: Nair, First name: Meera. Then print a name badge 20 characters wide with the full name centred and * as the fill character.
Show solution
#include <print>
#include <string>
int main() {
std::string full = "Meera Nair";
auto space = full.find(' ');
std::string first = full.substr(0, space);
std::string last = full.substr(space + 1);
std::println("Surname: {}, First name: {}", last, first);
std::println("{:*^20}", full);
}Surname: Nair, First name: Meera *****Meera Nair*****
The space is at index 5, so substr(0, 5) gives Meera and substr(6) gives Nair. The name is 10 characters, so 10 stars are split evenly, 5 on each side.
Quick Quiz
1. What does std::string("Jaipur").find('z') return?
When there is no match, find returns the special value std::string::npos.
2. Why is std::string_view a good parameter type for a function that only reads text?
A view is just a pointer and a length, so passing it is cheap; it never owns or extends the life of the text.
3. What does std::format("{:>6}", "ab") produce?
> right-aligns the text in a field 6 characters wide, adding 4 spaces on the left.
4. Which std::string member function was added in C++23?
starts_with and ends_with came in C++20, and contains in C++23.
Learn C++ with a mentor
Free lessons get you started. Our Java, C & C++ Programming training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
