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):
posis the index of the first character to copy. Indexes start at 0.countis how many characters to copy. It is a length, not the index where the slice ends.countis optional. Without it,substrcopies everything fromposto 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 tocountcharacters and stops at the end of the string. posequal to the size gives an empty string.language.substr(10)returns"".posgreater than the size throws.language.substr(11)throwsstd::out_of_rangewith the messagebasic_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
firstWordis the first 6 characters. They start at index 0 and there are 6 of them, so the call issubstr(0, 6), which gives"Coding".middleWordis 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.lastWordis 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)copiescountcharacters starting at indexpos.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
posbeyondsize()throwsstd::out_of_range. - The original string stays unchanged, so store the result.
- For the last
ncharacters, uses.substr(s.size() - n)and make surenis not larger than the string.
