Skip to content
Laviyx Learn

Try

C++ course menu

C++ Strings: std::string Operations, Searching, Substrings and Conversion

Learn std::string in C++: creating and joining strings, length and indexing, looping over characters, substr, find and replace, comparing strings, changing case, splitting with stringstream, and C-style strings vs std::string.

Beginner14 min readUpdated
On this page

Text is everywhere: names, messages, file contents, commands. In C++ you work with text using std::string from <string>. A std::string manages its own memory, grows as needed, and comes with methods for searching, slicing and modifying text.

Creating and joining strings#

C++
#include <iostream>
#include <string>

int main() {
    std::string first = "Asha";
    std::string last = "Rao";

    std::string full = first + " " + last;   // + joins strings
    full += "!";                             // += appends

    std::cout << full << '\n';
    std::cout << "Length: " << full.size() << '\n';
    std::cout << std::string(10, '-') << '\n';   // 10 copies of '-'
}
Output
Asha Rao!
Length: 9
----------

std::string(10, '-') builds a string of ten dashes. size() and length() are two names for the same thing.

Characters and indexing#

Each character has an index, starting at 0. s[i] reads or changes one character; s.at(i) does the same with a bounds check:

C++
#include <iostream>
#include <string>

int main() {
    std::string word = "cat";
    std::cout << word[0] << ' ' << word.back() << '\n';

    word[0] = 'b';
    std::cout << word << '\n';

    for (char c : word) {
        std::cout << c << '-';
    }
    std::cout << '\n';
}
Output
c t
bat
b-a-t-

Substrings with substr#

s.substr(start, count) returns a new string of count characters beginning at start. Leave out count to go to the end:

C++
#include <iostream>
#include <string>

int main() {
    std::string date = "2026-10-23";

    std::string year = date.substr(0, 4);
    std::string month = date.substr(5, 2);
    std::string day = date.substr(8);

    std::cout << day << '/' << month << '/' << year << '\n';
}
Output
23/10/2026

Searching with find#

s.find(text) returns the index of the first match, or the special value std::string::npos if there is none:

C++
#include <iostream>
#include <string>

int main() {
    std::string email = "asha.rao@example.com";

    std::size_t at = email.find('@');
    if (at != std::string::npos) {
        std::cout << "User: " << email.substr(0, at) << '\n';
        std::cout << "Domain: " << email.substr(at + 1) << '\n';
    }

    std::cout << std::boolalpha;
    std::cout << (email.find("xyz") == std::string::npos) << '\n';
    std::cout << email.ends_with(".com") << '\n';   // C++20
}
Output
User: asha.rao
Domain: example.com
true
true

Related methods: rfind searches from the end, and C++20 adds starts_with and ends_with. C++23 adds contains.

Modifying strings#

C++
#include <iostream>
#include <string>

int main() {
    std::string s = "I like tea";

    s.replace(s.find("tea"), 3, "coffee");   // replace 3 chars at that position
    std::cout << s << '\n';

    s.insert(0, "Yes, ");
    std::cout << s << '\n';

    s.erase(0, 5);                           // remove 5 chars from index 0
    std::cout << s << '\n';
}
Output
I like coffee
Yes, I like coffee
I like coffee

Comparing strings#

==, !=, < and > compare strings by their characters, in dictionary order (by character code, so uppercase letters come before lowercase ones):

C++
#include <iostream>
#include <string>

int main() {
    std::string a = "apple";
    std::string b = "banana";

    std::cout << std::boolalpha;
    std::cout << (a == "apple") << ' ' << (a < b) << ' ' << ("Zebra" < a) << '\n';
}
Output
true true true

"Zebra" < "apple" is true because 'Z' (90) has a smaller code than 'a' (97). To compare without caring about case, convert both strings to the same case first.

Changing case#

std::toupper and std::tolower (from <cctype>) work on one character at a time:

C++
#include <cctype>
#include <iostream>
#include <string>

std::string to_upper(std::string s) {
    for (char& c : s) {
        c = static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
    }
    return s;
}

int main() {
    std::cout << to_upper("Hello, C++!") << '\n';
}
Output
HELLO, C++!

Taking the parameter by value gives the function its own copy to change and return. The cast to unsigned char avoids undefined behaviour for characters outside the basic ASCII range.

Splitting a string#

std::string has no split method. A common approach reads words from a std::stringstream (from <sstream>), or uses std::getline with a separator:

C++
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

int main() {
    std::stringstream words("practice makes progress");
    std::string word;
    while (words >> word) {
        std::cout << "[" << word << "]";
    }
    std::cout << '\n';

    std::stringstream csv("Asha,Pune,21");
    std::vector<std::string> fields;
    std::string field;
    while (std::getline(csv, field, ',')) {
        fields.push_back(field);
    }
    std::cout << fields[1] << " has " << fields.size() << " fields\n";
}
Output
[practice][makes][progress]
Pune has 3 fields

Numbers and strings#

Convert with std::to_string, std::stoi and std::stod, as shown in Type Conversion. A std::stringstream can also build mixed text, much like std::cout:

C++
#include <iomanip>
#include <iostream>
#include <sstream>

int main() {
    std::ostringstream out;
    out << "Total: " << std::fixed << std::setprecision(2) << 1234.5;
    std::string text = out.str();
    std::cout << text << " (" << text.size() << " chars)\n";
}
Output
Total: 1234.50 (14 chars)

C-style strings#

A string literal like "hello" is actually a C-style string: an array of char ending with an invisible '\0' character. Old C code works with these through char* and functions like strlen. In C++, store text in std::string; it converts from literals automatically and avoids buffer-overflow bugs. If an old API needs a C-style string, s.c_str() provides one.

Common mistakes#

Practice#

Practice exercise

Write bool is_palindrome(const std::string& s) that ignores spaces and letter case, and test it with "Never odd or even" and "C++".

Show solution
C++
#include <cctype>
#include <iostream>
#include <string>

bool is_palindrome(const std::string& s) {
    std::string cleaned;
    for (char c : s) {
        if (c != ' ') {
            cleaned += static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
        }
    }
    for (std::size_t i = 0, j = cleaned.size(); i < j; ++i) {
        --j;
        if (cleaned[i] != cleaned[j]) return false;
    }
    return true;
}

int main() {
    std::cout << std::boolalpha;
    std::cout << is_palindrome("Never odd or even") << '\n';
    std::cout << is_palindrome("C++") << '\n';
}
Output
true
false

Practice exercise

Given std::string sentence = "the quick brown fox";, print it with the first letter of every word capitalized: The Quick Brown Fox.

Show solution
C++
#include <cctype>
#include <iostream>
#include <string>

int main() {
    std::string sentence = "the quick brown fox";

    bool new_word = true;
    for (char& c : sentence) {
        if (c == ' ') {
            new_word = true;
        } else if (new_word) {
            c = static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
            new_word = false;
        }
    }
    std::cout << sentence << '\n';
}
Output
The Quick Brown Fox

Key takeaway#

Finished this lesson?