Mastering String Slicing

Introduction: From One Character to a Whole Chunk

Welcome back! We know how to reach any single character inside a string. Useful as that is, real text work rarely stops at one character. We usually want a piece of text: the name part of a filename, its final few characters, or the year buried inside a timestamp.

Python's tool for that job is slicing, written as text[start:stop:step]. Same square brackets as before, just with colons separating up to three numbers. Our running example is a filename:

filename = "report_2024.csv"
Characterreport_2024.csv
Index01234567891011121314
Negative-15-14-13-12-11-10-9-8-7-6-5-4-3-2-1

Basic Slice Syntax: [start:stop]

Let's start with the two-number form, asking for characters from position 0 up to position 6:

filename = "report_2024.csv"

# [start:stop] includes start, excludes stop
print("Characters 0-5:", filename[0:6])

The single most important rule of slicing is that start is included and stop is excluded. So filename[0:6] collects indices 0 through 5, which spell "report"; index 6, the underscore, stays out. A handy side effect is that the length of the result equals stop - start — here, six characters.

Two more details: a slice builds a new string and leaves filename untouched, and a backward range such as filename[4:2] simply gives an empty string "" rather than an error.

Characters 0-5: report

Spelling Out Both Bounds

The two-number form really shines when the piece we want sits in the middle, with text on both sides. The year in our filename is a good example: it starts at index 7 and we want four characters, so we stop at 7 + 4 = 11.

# Both bounds spelled out to take a piece from the middle
print("Year:", filename[7:11])

This collects indices 7, 8, 9, and 10 — the characters 2, 0, 2, 4 — giving "2024". Note how the exclusive stop works for us here: 11 is the index of the dot, and naming it as the stop keeps it out of the result. A useful way to picture it is that the numbers mark the gaps between characters rather than the characters themselves, so [7:11] means "everything between gap 7 and gap 11."

Year: 2024

Omitting Bounds to Reach the Edges

Writing 0 as the start feels redundant, and Python agrees: either bound can be left out, and it snaps to the nearest edge of the string. No start means "from the very beginning," no stop means "through the very end."

# Omitting a bound runs to the edge of the string
print("Prefix:", filename[:6])
print("Suffix:", filename[7:])

This gives us the classic prefix/suffix pair:

  • filename[:6] is identical to filename[0:6], so we get the prefix "report" with less typing.
  • filename[7:] starts at index 7, the 2 in 2024, and runs all the way to the final v, producing "2024.csv".

Leaving out both bounds, as in filename[:], asks for everything: a full copy of the string.

Prefix: report
Suffix: 2024.csv

Negative Indices Inside Slices

As you may recall from Unit 1, negative indices count backward from the right, with -1 as the last character. Those same numbers work as slice bounds, which makes end-of-string work delightfully short:

# Negative indices in slices count from the end
print("Last three:", filename[-3:])

filename[-3:] starts three characters from the end and runs through the end, giving "csv". Two reusable patterns are worth memorizing:

  • text[-N:] means "the last N characters."
  • text[:-N] means "everything except the last N characters," so filename[:-4] gives "report_2024".

Neither form needs len() first, so the same expression works for "a.csv" or a filename with fifty characters.

An honest caveat about file extensions. For this filename, the last three characters happen to be the extension, csv. But that is a fact about our example, not a general rule: "script.py" has a two-character extension, "photo.jpeg" has four, "README" has none at all, and "data.tar.gz" has two suffixes stacked. [-3:] would quietly return the wrong text in every one of those cases. Real code finds the dot instead of assuming a width — in Unit 4 we will meet find(), which locates a character for us, and Python's standard library offers pathlib.Path(filename).suffix for exactly this job. For now, treat [-3:] as "the last three characters," which is all it really promises.

Last three: csv

Adding a Step: [start:stop:step]

Now for the third slot in text[start:stop:step]. The step value tells Python how far to jump after taking each character; it defaults to 1, which is why every slice so far picked up neighbours. Setting it to 2 takes every second character instead:

# A step selects every nth character
print("Every second char:", filename[::2])

Both bounds are omitted, so we walk the whole string, and the step of 2 visits indices 0, 2, 4, 6, 8, 10, 12, and 14. Reading those off the table gives r, p, r, _, 0, 4, c, v, joined into "rpr_04cv". A step of 3 would visit 0, 3, 6, and so on; any positive whole number works.

Every second char: rpr_04cv

Reversing with a Negative Step

A step can also be negative, which flips the direction of travel: Python walks the string from right to left. This turns string reversal into a single expression:

# A negative step reverses the string
print("Reversed:", filename[::-1])

Starting from the last character, v, and stepping backward one position at a time, we collect every character in reverse order, ending on the leading r. Here is the gotcha worth remembering: with a negative step the default bounds flip too, so an omitted start means "the end of the string" and an omitted stop means "past the beginning."

That is why [::-1] is the reliable reversal idiom, while a hopeful guess such as filename[0:15:-1] returns an empty string — we would be asking Python to move left from index 0, and there is nothing there.

Reversed: vsc.4202_troper

Slice Patterns Cheat Sheet

Most day-to-day slicing comes down to a handful of shapes:

PatternMeaningExample on filename
[a:b]From a up to, but not including, b[7:11] gives "2024"
[:n]The first n characters[:6] gives "report"
[n:]Everything from n to the end[7:] gives "2024.csv"
[-n:]The last n characters[-3:] gives "csv"
[:-n]Everything except the last n[:-4] gives "report_2024"
[::k]Every k-th character[::2] gives "rpr_04cv"
[::-1]The whole string reversed"vsc.4202_troper"

Recognizing these shapes on sight is the real goal; the rest is filling in numbers that fit the task at hand.

Conclusion and Next Steps

One more comforting property before we practise: unlike indexing, slicing never raises an out-of-range error. It quietly clips to what exists, so filename[:500] returns the whole string rather than crashing, and even an empty string survives every slice in the table above.

In Unit 3 we shift from carving text apart to reshaping it with string methods such as upper, strip, and replace. Time to slice!

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal