Remove Special Characters Except Space From a String in Python

If you’ve tried stripping symbols with isalnum() or \W, you’ve probably watched the spaces vanish too, leaving Greatproduct55. To remove special characters except space in Python, use re.sub(r"[^A-Za-z0-9 ]+", "", text).

Why? A space isn’t a letter or a digit, so both checks delete it like any other symbol.

In this article, I’ll show you three ways to keep the spaces, plus how to tidy tabs and double spaces. I ran every snippet on Python 3.12.5.

The sample strings with special characters

Every example imports these five customer reviews from reviews.py:

reviews = [
    "Great product!!! 5/5 :)",
    "Shipped to Austin, TX -- fast & cheap ($19.99)",
    "Café latte was perfect!",
    "Order #4521\twas late",
    "Loved Brew™ beans — 10/10",
]
  • Punctuation and symbols: !!!, #, $, &
  • An accented letter in Café
  • A tab between 4521 and was
  • An em dash and a trademark sign, which aren’t ASCII

The methods disagree on those last three. To only detect symbols, see checking a string for special characters.

Method 1: Remove special characters except space with re.sub() (Recommended)

A negated character class is the shortest fix: ^ inside the brackets means “anything not listed”, and the space is listed. Here’s the code, after the broken isalnum() attempt:

from reviews import reviews
import re

print("".join(c for c in reviews[0] if c.isalnum()))   # spaces gone
for text in reviews:
    print(repr(re.sub(r"[^A-Za-z0-9 ]+", "", text)))

Output:

Greatproduct55
'Great product 55 '
'Shipped to Austin TX  fast  cheap 1999'
'Caf latte was perfect'
'Order 4521was late'
'Loved Brew beans  1010'

The first line shows the bug. The regex keeps every space, though Café loses its é.

Command Prompt output of Python re.sub removing special characters except space from five customer reviews
isalnum() alone glues the words together, while re.sub keeps the spaces and strips the symbols.

How does this code work?

[^A-Za-z0-9 ]+ matches any run of characters that aren’t ASCII letters, digits or a space, and re.sub() deletes each run.

So here’s the catch: A-Za-z is ASCII only, so é, ™ and the tab are all removed. More on patterns in regular expressions in Python.

Pro Tip: Need to keep prices? Add the characters to the class: [^A-Za-z0-9 $.]+ keeps $19.99 intact.

Method 2: Keep letters, digits and spaces with isalnum()

If your text has accented names, use isalnum() and allow the space explicitly. Same five reviews here:

from reviews import reviews
for text in reviews:
    print(repr("".join(c for c in text if c.isalnum() or c == " ")))

Output:

'Great product 55 '
'Shipped to Austin TX  fast  cheap 1999'
'Café latte was perfect'
'Order 4521was late'
'Loved Brew beans  1010'
Command Prompt output of Python isalnum with a space check keeping accented letters in customer reviews
The isalnum() version keeps Café with its accent and removes the em dash and trademark sign.

How does this code work?

The generator checks each character. c.isalnum() is Unicode-aware, so é passes, while c == " " lets plain spaces through.

Everything else, including the tab, is dropped. The underscore fails isalnum() too, unlike the alphanumeric-and-underscore check.

Method 3: Delete ASCII punctuation from a string with str.translate()

str.translate() deletes every character in string.punctuation in one fast pass. Let me show you how it works:

from reviews import reviews
import string

table = str.maketrans("", "", string.punctuation)
for text in reviews:
    print(repr(text.translate(table)))

Output:

'Great product 55 '
'Shipped to Austin TX  fast  cheap 1999'
'Café latte was perfect'
'Order 4521\twas late'
'Loved Brew™ beans — 1010'
Command Prompt output of Python str.translate deleting string.punctuation while keeping spaces, tab and em dash
translate() strips the ASCII punctuation but leaves the tab, the em dash and the trademark sign in place.

How does this code work?

str.maketrans("", "", string.punctuation) builds a table that maps 32 ASCII symbols to nothing. Spaces aren’t in that set, so they stay.

Neither are the tab, — or ™. It’s great for removing punctuation from strings, not for every special character.

What about tabs, newlines and double spaces?

Removing -- and & left double spaces, and the tab glued 4521was. Use \s to keep all whitespace, then collapse it:

from reviews import reviews
import re

for text in reviews:
    clean = re.sub(r"[^A-Za-z0-9\s]+", "", text)   # \s keeps tabs too
    print(repr(" ".join(clean.split())))

Output:

'Great product 55'
'Shipped to Austin TX fast cheap 1999'
'Caf latte was perfect'
'Order 4521 was late'
'Loved Brew beans 1010'
Command Prompt output of Python re.sub with \s and split join collapsing double spaces and tabs in reviews
Keeping \s and then joining split() words gives one clean space between every word.

How does this code work?

\s keeps tabs and newlines, and " ".join(clean.split()) turns any whitespace run into one space. The same trick appears in replacing multiple spaces with a single space.

Picking the right method to remove special characters

MethodKeeps éUse it when
re.sub(r"[^A-Za-z0-9 ]+", "", s)NoASCII text; easy to allow extra characters
isalnum() or c == " "YesNames and text in other languages
str.translate()YesOnly ASCII punctuation needs to go

Things to Keep in Mind When Removing Special Characters

  • A literal space in the pattern keeps spaces only; use \s if tabs and newlines matter.
  • A-Za-z drops accented letters. Switch to isalnum() for names like Café.
  • string.punctuation covers 32 ASCII symbols, not em dashes, emoji or ™.

A few more string clean-up guides from the site:

Frequently asked questions

How do I remove special characters but keep spaces in Python?

Use re.sub(r"[^A-Za-z0-9 ]+", "", text). The space inside the brackets is what keeps it.

Why does isalnum() remove spaces?

A space isn’t a letter or digit, so " ".isalnum() is False. Add or c == " " to keep it.

How do I keep accented letters like é?

Use isalnum() with a space check, or the regex [^\w ]+, which also keeps underscores.

How do I keep tabs and newlines too?

Use \s in the class: [^A-Za-z0-9\s]+.

How do I remove the double spaces left behind?

Run " ".join(text.split()) after removing the symbols.

Where is re.sub() documented?

See re.sub() in the Python docs.