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.

Alex
  • 1,360
  • 2
  • 12
  • 18

5 Answers5

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."
bobince
  • 528,062
  • 107
  • 651
  • 834
  • 7
    That is infinately better than my solution. I also hope to become immortal one day. – Tor Valamo Dec 14 '09 at 05:28
  • 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 Dec 14 '09 at 22:10
  • thanks, didn't know about using no argument for runs of whitespace. Huge!! – MattoTodd Feb 04 '11 at 00:25
  • 3
    This is still faster than regex for a 20MB string. – Kos Aug 04 '11 at 19:35
  • Great, saved me time and code - but will this .split behaviour remain in the future ? – Dominique Guardiola Aug 28 '11 at 17:15
  • 1
    @Dominique: yes, it's a [documented](http://docs.python.org/library/stdtypes.html#str.split) 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. – bobince Aug 28 '11 at 22:10
  • to further enhance my joy at finding this, this function also strips extraneous whitespace at the start and end of the string. – tehwalrus Nov 07 '11 at 15:45
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."
Imran
  • 87,203
  • 23
  • 98
  • 131
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.

John Machin
  • 81,303
  • 11
  • 141
  • 189
  • 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 Dec 14 '09 at 22:21
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."
Tor Valamo
  • 33,261
  • 11
  • 73
  • 81
  • see the edit. also, which python version are you using? you need 2.6 for the None argument to work. – Tor Valamo Dec 14 '09 at 03:05
  • Yeah, I'm using 2.5... is there an alternative for None? Otherwise I'll have to use the other answer... – Alex Dec 14 '09 at 03:08
  • Nice, thanks very much! This is the best answer now, especially since it caters for my 2.5-ness. – Alex Dec 14 '09 at 03:24
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."
miku
  • 181,842
  • 47
  • 306
  • 310
  • I originally had this as the first answer; it was a nice solution and good use of python simplicity :) – Alex Dec 14 '09 at 03:27