views:

1965

answers:

5

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.

+1  A: 

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."
The MYYN
I originally had this as the first answer; it was a nice solution and good use of python simplicity :)
Alex
+1  A: 

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."
Tor Valamo
Doesn't seem to work... would be nice if it did though!
Alex
see the edit. also, which python version are you using? you need 2.6 for the None argument to work.
Tor Valamo
Yeah, I'm using 2.5... is there an alternative for None? Otherwise I'll have to use the other answer...
Alex
yes, you can use string.maketrans('','') instead of None.
Tor Valamo
Nice, thanks very much! This is the best answer now, especially since it caters for my 2.5-ness.
Alex
+20  A: 

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."
bobince
That is infinately better than my solution. I also hope to become immortal one day.
Tor Valamo
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 :)
Alex
+5  A: 

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."
Imran
Nothing, good solution. I think the .join/split option is pretty neat though, don't you think? :)
Alex
Indeed. In fact, `timeit` shows join/split to be it's 6 times faster than re.sub() for your given string.
Imran
+3  A: 

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.

John Machin
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.
Alex