Strings, string_view & format

C++ · Lesson 5 of 32 6 min read Updated

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

C++
#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);
}
Output
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.

C++
#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");
    }
}
Output
@ 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

C++
#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);
}
Output
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.

C++
#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));
}
Output
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.

C++
#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);
}
Output
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 old char* strings it compares memory addresses, not text.
  • Forgetting that indexes start at 0. The last character is at size() - 1.
  • Not checking find. Using npos as a position, as in substr(npos), throws an error. Compare with std::string::npos first.
  • Counting rupee signs and Hindi letters. size() counts bytes, and in UTF-8 text a character such as the rupee sign uses 3 bytes, so size() can be larger than the number of visible characters.

Key points

  • std::string stores text; join with +, append with +=, and read characters with [] or at().
  • find, substr, starts_with, ends_with and C++23's contains cover most searching needs.
  • std::stoi, std::stod and std::to_string convert between text and numbers.
  • std::string_view is a cheap, read-only view, ideal for function parameters, but it must not outlive its text.
  • std::format and std::println share 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
C++
#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);
}
Output
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?

2. Why is std::string_view a good parameter type for a function that only reads text?

3. What does std::format("{:>6}", "ab") produce?

4. Which std::string member function was added 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