Remove the Last Character From a String in Python

To remove the last character from a string in Python, slice up to -1:

text = "Hello World!"

text[:-1]       # 'Hello World'
text[:-3]       # 'Hello Wor'   the last three

Two traps follow from that. Writing text[:-n] with n as a variable empties the string when n is zero, and rstrip removes more characters than you expect.

Both are demonstrated below, on Python 3.12.5.

Removing the last character with slicing

A negative index counts back from the end, so you never need len():

text = "Hello World!"

print("original :", repr(text))
print("[:-1]    :", repr(text[:-1]))
print("[:-3]    :", repr(text[:-3]), "  the last three")

print()
# a negative index counts back from the end, so no len() needed
print("last character is:", repr(text[-1]))
print("without it       :", repr(text[:-1]))

Output:

original : 'Hello World!'
[:-1]    : 'Hello World'
[:-3]    : 'Hello Wor'   the last three

last character is: '!'
without it       : 'Hello World'

text[:-1] means “everything up to but not including the last character”. text[-1] is that character on its own.

For a fixed number, just write the number: text[:-2] drops the last two, text[:-3] the last three.

Why text[:-n] empties the string when n is zero

This is the bug worth knowing about, because it destroys data rather than raising:

text = "Hello World!"

def remove_last(s, n):
    return s[:-n]              # looks fine, and it is broken


for n in (3, 2, 1, 0):
    print(f"n={n} -> {remove_last(text, n)!r}")

print()
print("n=0 wiped the whole string, because -0 is 0 and s[:0] is empty")

print()
def remove_last_safe(s, n):
    return s[:len(s) - n]

print("the safe version:")
for n in (3, 2, 1, 0):
    print(f"n={n} -> {remove_last_safe(text, n)!r}")

Output:

n=3 -> 'Hello Wor'
n=2 -> 'Hello Worl'
n=1 -> 'Hello World'
n=0 -> ''

n=0 wiped the whole string, because -0 is 0 and s[:0] is empty

the safe version:
n=3 -> 'Hello Wor'
n=2 -> 'Hello Worl'
n=1 -> 'Hello World'
n=0 -> 'Hello World!'
Command Prompt showing that slicing with a negative zero count returns an empty Python string
n=0 returns nothing at all, while n=1 through 3 work correctly.

The reason is that -0 and 0 are the same number. text[:-0] is text[:0], which is an empty string.

Any helper written as return s[:-n] carries this bug. It works in every test where n is at least one, then wipes a value in production.

s[:len(s) - n] is the safe form. It behaves correctly for zero and for every positive count.

Why does rstrip remove too many characters?

rstrip looks like the obvious tool and it does something different:

print("rstrip removes EVERY trailing match, not one:")
print("  'banana'.rstrip('a')  ->", repr("banana".rstrip("a")))
print("  'test...'.rstrip('.') ->", repr("test...".rstrip(".")))

print()
print("slicing and removesuffix remove exactly one:")
print("  'test...'[:-1]            ->", repr("test..."[:-1]))
print("  'test...'.removesuffix('.') ->", repr("test...".removesuffix(".")))

print()
# and removesuffix does nothing if the string does not end that way
print("  'hello'.removesuffix('xyz') ->", repr("hello".removesuffix("xyz")), " unchanged")

Output:

rstrip removes EVERY trailing match, not one:
  'banana'.rstrip('a')  -> 'banan'
  'test...'.rstrip('.') -> 'test'

slicing and removesuffix remove exactly one:
  'test...'[:-1]            -> 'test..'
  'test...'.removesuffix('.') -> 'test..'

  'hello'.removesuffix('xyz') -> 'hello'  unchanged
Command Prompt comparing rstrip removesuffix and slicing when removing trailing characters in Python
'test...'.rstrip('.') removes all three dots. The slice removes one.
MethodOn 'test...'Removes
text[:-1]'test..'Exactly one character
text.rstrip(".")'test'Every trailing dot
text.removesuffix(".")'test..'One, only if it matches

removesuffix arrived in Python 3.9 and is the precise tool. It removes one occurrence, and leaves the string alone if it doesn’t end that way.

That safety is the difference. rstrip takes a set of characters, which is the same behaviour that surprises people when removing newlines.

Removing the last character from an empty string

Slicing is safe on an empty string. Indexing is not:

# slicing an empty string is safe, indexing it is not
empty = ""

print('""[:-1]  ->', repr(empty[:-1]), "  no error")

try:
    print(empty[-1])
except IndexError as err:
    print('""[-1]   ->', type(err).__name__ + ":", err)

print()
print("so text[:-1] needs no length check, but text[-1] does")

Output:

""[:-1]  -> ''   no error
""[-1]   -> IndexError: string index out of range

so text[:-1] needs no length check, but text[-1] does

""[:-1] returns an empty string quietly, so you can call it without checking the length first.

""[-1] raises IndexError: string index out of range. If you need the last character rather than the string without it, check first.

Removing a trailing newline from a Python string

This is the single most common reason people want the last character gone, and slicing is the wrong tool for it:

unix = "value\n"
windows = "value\r\n"

print("unix    [:-1]  :", repr(unix[:-1]))
print("windows [:-1]  :", repr(windows[:-1]), "  <- carriage return left behind")

print()
print("unix    rstrip :", repr(unix.rstrip()))
print("windows rstrip :", repr(windows.rstrip()), "  both handled")

print()
print("use rstrip() for line endings, [:-1] only when you know the text is clean")

Output:

unix    [:-1]  : 'value'
windows [:-1]  : 'value\r'   <- carriage return left behind

unix    rstrip : 'value'
windows rstrip : 'value'   both handled

use rstrip() for line endings, [:-1] only when you know the text is clean
Command Prompt showing that slicing off the last character leaves a carriage return in Windows text
[:-1] on Windows text leaves 'value\r' behind.

A Windows line ending is two characters, \r\n. Removing one leaves the carriage return, which is invisible and breaks comparisons.

rstrip() with no argument removes all trailing whitespace including both characters, which is exactly right here.

Removing a trailing comma or slash in Python

The practical cases are nearly always a separator that got added one time too many:

# the classic: a trailing separator built up in a loop
parts = ["apple", "banana", "cherry"]

built = ""
for item in parts:
    built += item + ", "

print("built    :", repr(built))
print("trimmed  :", repr(built[:-2]), "  removes the comma AND the space")

print()
# which is why join exists
print("join     :", repr(", ".join(parts)), "  no trailing separator to remove")

print()
# trailing slash on a path or URL
url = "https://example.com/api/"
print("url      :", repr(url.rstrip("/")))

Output:

built    : 'apple, banana, cherry, '
trimmed  : 'apple, banana, cherry'   removes the comma AND the space

join     : 'apple, banana, cherry'   no trailing separator to remove

url      : 'https://example.com/api'
Command Prompt showing a trailing comma removed from a Python string built in a loop
built[:-2] removes the comma and the space together.

Count the characters you are removing. A ", " separator is two, so [:-1] leaves a stray space behind.

Better still, don’t create the problem. ", ".join(parts) puts separators only between items, so there is nothing to trim.

For a trailing slash on a URL or path, rstrip("/") is right, because it also handles the case where there are two. It pairs with taking the first n characters.

Common mistakes removing the last character

SymptomCauseFix
String came back emptys[:-n] with n = 0Use s[:len(s) - n]
Too many characters gonerstrip strips all matchesUse removesuffix
A stray space leftSeparator was two charactersUse [:-2]
Comparison still failsWindows \r\nUse rstrip()
IndexErrorUsed text[-1] on emptyUse text[-1:]
Nothing changedResult not assignedtext = text[:-1]

More Python string trimming guides:

Frequently asked questions

How do I remove the last character from a string in Python?

Slice it: text[:-1]. Negative indexing is explained in the Python strings tutorial.

How do I remove the last two characters?

text[:-2]. Any fixed count works the same way.

Why does text[:-n] return an empty string?

Because n is zero, and -0 is 0, so the slice becomes text[:0]. Use text[:len(text) - n] instead.

What is the difference between rstrip and removesuffix?

rstrip removes every trailing character in the set you give it. removesuffix removes one occurrence, and only if the string ends with it.

How do I remove a trailing newline?

text.rstrip(). Slicing off one character leaves the carriage return behind in Windows text.

Does removing the last character work on an empty string?

Yes. ""[:-1] returns an empty string with no error, unlike ""[-1].

How do I remove a trailing comma?

text[:-1] for a bare comma, or text[:-2] if the separator was a comma and a space. Using join avoids the problem entirely.