49

Python's string.whitespace is great:

>>> string.whitespace
'\t\n\x0b\x0c\r '

How do I use this with a string without resorting to manually typing in '\t|\n|... etc for regex?

For example, it should be able to turn: "Please \n don't \t hurt \x0b me."

into

"Please don't hurt me."

I'd probably want to keep the single spaces, but it'd be easy enough to just go string.whitespace[:-1] I suppose.

5 Answers 5

148

There is a special-case shortcut for exactly this use case!

If you call str.split without an argument, it splits on runs of whitespace instead of single characters. So:

>>> ' '.join("Please \n don't \t hurt \x0b me.".split())
"Please don't hurt me."
Sign up to request clarification or add additional context in comments.

7 Comments

That is infinately better than my solution. I also hope to become immortal one day.
Wow. That is amazing. Perfect for what I'm doing, since they're small strings. I wonder how this would perform on large datasets though? It'd be great if anyone knows how it works intrinsicly :)
thanks, didn't know about using no argument for runs of whitespace. Huge!!
This is still faster than regex for a 20MB string.
@Dominique: yes, it's a documented stdlib feature—“If sep is not specified or is None, a different splitting algorithm is applied...”—that's widely used and not likely to be deprecated.
|
14

What's wrong with the \s character class?

>>> import re

>>> pattern = re.compile(r'\s+')
>>> re.sub(pattern, ' ', "Please \n don't \t hurt \x0b me.")
"Please don't hurt me."

3 Comments

Nothing, good solution. I think the .join/split option is pretty neat though, don't you think? :)
Indeed. In fact, timeit shows join/split to be it's 6 times faster than re.sub() for your given string.
I suppose once compiled and sub reused multiple times this could be fast too
9

Let's make some reasonable assumptions:

(1) You really want to replace any run of whitespace characters with a single space (a run is of length 1 or greater).

(2) You would like the same code to work with minimal changes under Python 2.X with unicode objects.

(3) You don't want your code to assume things that are not guaranteed in the docs

(4) You would like the same code to work with minimal changes with Python 3.X str objects.

The currently selected answer has these problems:

(a) changes " " * 3 to " " * 2 i.e. it removes duplicate spaces but not triplicate, quadruplicate, etc spaces. [fails requirement 1]

(b) changes "foo\tbar\tzot" to "foobarzot" [fails requirement 1]

(c) when fed a unicode object, gets TypeError: translate() takes exactly one argument (2 given) [fails requirement 2]

(d) uses string.whitespace[:-1] [fails requirement 3; order of characters in string.whitespace is not guaranteed]

(e) uses string.whitespace[:-1] [fails requirement 4; in Python 2.X, string.whitespace is '\t\n\x0b\x0c\r '; in Python 3.X, it is ' \t\n\r\x0b\x0c']

The " ".join(s.split()) answer and the re.sub(r"\s+", " ", s) answer don't have these problems.

1 Comment

Hey, you raise some great points. For me, the ' '.join(s.split()) works on the "foo\tbar\tzot" test! I mean, the original answer worked for me, but that's only because I'm not expecting such weird strings. However something that deals with this would be great. I just tested the sub with "foo\tbar\tzot" and it works... so I guess I'm just choosing the ' '.join(s.split()) version due to its simplicity and being able to work without importing the re module. Also my datasets are small, so I'm not worried about performance issues, if there were any.
2

You could use the translate method

import string

s = "Please \n don't \t hurt \x0b me."
s = s.translate(None, string.whitespace[:-1]) # python 2.6 and up
s = s.translate(string.maketrans('',''), string.whitespace[:-1]) # python 2.5, dunno further down
>>> s
"Please  don't  hurt  me."

And then remove duplicate whitespace

s.replace('  ', ' ')
>>> s
"Please don't hurt me."

3 Comments

see the edit. also, which python version are you using? you need 2.6 for the None argument to work.
Yeah, I'm using 2.5... is there an alternative for None? Otherwise I'll have to use the other answer...
Nice, thanks very much! This is the best answer now, especially since it caters for my 2.5-ness.
1

a starting point .. (although it's not shorter than manually assembling the whitespace circus) ..

>>> from string import whitespace as ws
>>> import re

>>> p = re.compile('(%s)' % ('|'.join([c for c in ws])))
>>> s = "Please \n don't \t hurt \x0b me."

>>> p.sub('', s)
"Pleasedon'thurtme."

Or if you want to reduce whitespace to a maximum of one:

>>> p1 = re.compile('(%s)' % ('|'.join([c for c in ws if not c == ' '])))
>>> p2 = re.compile(' +')
>>> s = "Please \n don't \t hurt \x0b me."

>>> p2.sub(' ', p1.sub('', s))
"Please don't hurt me."

Third way, more compact:

>>> import string

>>> s = "Please \n don't \t hurt \x0b me."
>>> s.translate(None, string.whitespace[])
"Pleasedon'thurtme."

>>> s.translate(None, string.whitespace[:5])
"Please  don't  hurt  me."

>>> ' '.join(s.translate(None, string.whitespace[:5]).split())
"Please don't hurt me."

1 Comment

I originally had this as the first answer; it was a nice solution and good use of python simplicity :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.