Start learning free

LESSON 57 OF 409 · STRINGS I

String Slicing in C++: substr() Explained

Lesson Read the full explanation

Not only we can select a single character from a string, but we can also select a sequence of consecutive characters (e.g. substring) from a string.

To achieve this, we use the substr() method like this:

string.substr(startIndex, length);

This is called slicing a string. It returns the substring that starts at startIndex and has size length.

Here is an example:

string language = "JavaScript";
string substring = language.substr(2, 4);

cout << substring << endl; // Output: "vaSc"
cout << language << endl; // Output: "JavaScript"

Notice that language preserved its value. The slicing does not affect the original string. It just creates a brand new one representing the sliced substring.


If you omit the length, the substr() extracts to the end of the string:

string language = "JavaScript";
string substring = language.substr(4);

cout << substring; // Output: "Script"

Assignment
Follow the Coding Tutorial and let's slice some strings!


Hint
Look at the examples above if you get stuck.


STEP 1 OF 3
    Ctrl + Enter to check

    C++ · STRINGS I

    String Slicing in C++, explained

    Slicing a string in C++

    C++ has no slice operator like s[2:6]. To slice a string, which means to copy out a run of consecutive characters, you call the member function substr() of std::string. The result is a new string holding that run. This is how you get a word out of a sentence, a file extension out of a name, or the first few characters of an id.

    The signature is s.substr(pos, count):

    • pos is the index of the first character to copy. Indexes start at 0.
    • count is how many characters to copy. It is a length, not the index where the slice ends.
    • count is optional. Without it, substr copies everything from pos to the end of the string.
    string language = "JavaScript";
    //                 J a v a S c r i p t
    //                 0 1 2 3 4 5 6 7 8 9
    
    string substring = language.substr(2, 4);   // "vaSc": 4 characters, starting at index 2
    string rest = language.substr(4);           // "Script": from index 4 to the end
    
    cout << substring << endl;  // vaSc
    cout << rest << endl;       // Script
    cout << language << endl;   // JavaScript
    

    The original string is never changed. substr is a const member function that returns a fresh copy, so you must store the result or print it. A line like language.substr(2, 4); on its own does nothing useful, and compilers warn that the return value is ignored. To shrink a string itself, assign the slice back: language = language.substr(0, 4); leaves "Java".

    The second argument is a length, not an end index

    This is the detail that trips up most learners. Many languages slice with a start and an end, such as Python's s[2:6] or Java's s.substring(2, 6). In C++ the same slice, "vaSc", is substr(2, 4). If you know the start and end indexes, convert them first:

    size_t start = 2, end = 6;                    // end is the index just after the slice
    string part = language.substr(start, end - start);  // "vaSc"
    

    Passing the end index by mistake still compiles and runs, which is what makes it a silent bug: language.substr(2, 6) returns "vaScri" instead of "vaSc".

    What happens at the edges

    • A count that is too long is trimmed. language.substr(4, 100) returns "Script". It copies up to count characters and stops at the end of the string.
    • pos equal to the size gives an empty string. language.substr(10) returns "".
    • pos greater than the size throws. language.substr(11) throws std::out_of_range with the message basic_string::substr: __pos (which is 11) > this->size() (which is 10). Unhandled, the program stops.

    Taking the last characters

    There are no negative indexes in C++. To take the last n characters, compute the start from size():

    string message = "Coding feels great";
    size_t n = 5;
    string tail = message.substr(message.size() - n);   // "great"
    

    size() returns an unsigned type, so if n is larger than the string, message.size() - n wraps around to a huge number instead of going negative. Then substr throws out_of_range. Check n <= message.size() first when n comes from user input.

    Slicing around a separator

    Combine find with substr to cut at a character. find returns string::npos when it finds nothing, so test for that before adding 1:

    string path = "dir/file.txt";
    size_t slash = path.find('/');
    if (slash != string::npos) {
        cout << path.substr(slash + 1) << endl;   // file.txt
    }
    

    Every substr allocates and copies. When you only need to look at a part of a long string, a std::string_view (C++17) has its own substr that copies nothing. It points into the original text, so the original must stay alive and unchanged while you use it.

    The exercise, step by step

    The lesson gives you message = "Coding feels great" and asks for three slices. Count the indexes first:

    C o d i n g   f e e l s   g r e a t
    0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
    
    1. firstWord is the first 6 characters. They start at index 0 and there are 6 of them, so the call is substr(0, 6), which gives "Coding".
    2. middleWord is the 8th through 12th characters. Indexes start at 0, so the 8th character is at index 7. From the 8th through the 12th is 5 characters, so the length is 5: substr(7, 5) gives "feels". Passing 12 as the second argument would be the end-index mistake, and it would copy 12 characters.
    3. lastWord is the last 5 characters. The string has 18 characters, so they start at index 13. With no length given, substr(13) runs to the end and gives "great".
    string message = "Coding feels great";
    
    string firstWord = message.substr(0, 6);
    string middleWord = message.substr(7, 5);
    string lastWord = message.substr(13);
    

    Print them to check: cout << firstWord << "|" << middleWord << "|" << lastWord << endl; prints Coding|feels|great. The lesson writes string and cout without std::. In a file of your own, add using namespace std; or write std::string and std::cout, and include <string> and <iostream>.

    Key takeaways

    • s.substr(pos, count) copies count characters starting at index pos. s.substr(pos) copies to the end.
    • The second argument is a length. Subtract the start from the end index when you have one.
    • A count that runs past the end is trimmed, but a pos beyond size() throws std::out_of_range.
    • The original string stays unchanged, so store the result.
    • For the last n characters, use s.substr(s.size() - n) and make sure n is not larger than the string.
    Keep going with C++About 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free