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 é.

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'

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'

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'

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
| Method | Keeps é | Use it when |
|---|---|---|
re.sub(r"[^A-Za-z0-9 ]+", "", s) | No | ASCII text; easy to allow extra characters |
isalnum() or c == " " | Yes | Names and text in other languages |
str.translate() | Yes | Only ASCII punctuation needs to go |
Things to Keep in Mind When Removing Special Characters
- A literal space in the pattern keeps spaces only; use
\sif tabs and newlines matter. A-Za-zdrops accented letters. Switch toisalnum()for names like Café.string.punctuationcovers 32 ASCII symbols, not em dashes, emoji or ™.
A few more string clean-up guides from the site:
- Remove a character from a string in Python
- Remove a character by index
- Split a string by space in Python
- Split a string into characters
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.

Bijay Kumar is a 13-time Microsoft MVP with more than 18 years in software development, and the founder of Python Guides and TSinfo Technologies. He started out building .NET and SharePoint solutions at HP, TCS and KPIT before moving into Python, machine learning and AI, and he also builds web apps with TypeScript and React. He writes the tutorials here himself, and every example is run before publishing so you see the real output. More about Bijay · Microsoft MVP profile · LinkedIn