Простой способ удалить несколько пробелов в строке? - PullRequest
300 голосов
/ 10 октября 2009

Предположим, это строка:

The   fox jumped   over    the log.

Это приведет к:

The fox jumped over the log.

Какой самый простой 1-2 лайнер может это сделать? Не разбивая и не заходя в списки ...

Ответы [ 21 ]

455 голосов
/ 10 октября 2009

foo это ваша строка:

" ".join(foo.split())

Имейте в виду, что при этом удаляются «все пробельные символы (пробел, табуляция, новая строка, возврат, подача формы)». (Благодаря hhsaffar , см. Комментарии), т. Е. "this is \t a test\n" будет эффективно заканчиваться как "this is a test"

402 голосов
/ 10 октября 2009
>>> import re
>>> re.sub(' +', ' ', 'The     quick brown    fox')
'The quick brown fox'
78 голосов
/ 10 октября 2009
import re
s = "The   fox jumped   over    the log."
re.sub("\s\s+" , " ", s)

или

re.sub("\s\s+", " ", s)

, так как пробел перед запятой указан как любимая мозоль в PEP8, как упоминает лось в комментариях.

48 голосов
/ 10 апреля 2013

Использование регулярных выражений с "\ s" и выполнение простых завязываний string.split () также удаление других пробелов - таких как переносы строк, возврат каретки, вкладки. Если это не требуется, чтобы только сделать несколько пробелов , я приведу эти примеры.


РЕДАКТИРОВАТЬ: Как я обычно делаю, я спал на этом, и помимо исправления опечатки на последних результатах (v3.3.3 @ 64-бит, не 32 -бит), меня поразило очевидное: тестовая строка была довольно тривиальной.

Итак, я получил ... 11 абзацев, 1000 слов, 6665 байт Lorem Ipsum , чтобы получить более реалистичные временные тесты. Затем я добавил лишние пробелы произвольной длины:

original_string = ''.join(word + (' ' * random.randint(1, 10)) for word in lorem_ipsum.split(' '))

Я также исправил "правильное join"; если кто-то заботится, то одна строка по существу сделает полосу любых начальных / конечных пробелов, эта исправленная версия сохраняет начальную / конечную пробелы (но только ONE ;-). (Я нашел это, потому что случайно расположенный lorem_ipsum получил дополнительные пробелы в конце и таким образом потерпел неудачу assert.)


# setup = '''

import re

def while_replace(string):
    while '  ' in string:
        string = string.replace('  ', ' ')

    return string

def re_replace(string):
    return re.sub(r' {2,}' , ' ', string)

def proper_join(string):
    split_string = string.split(' ')

    # To account for leading/trailing spaces that would simply be removed
    beg = ' ' if not split_string[ 0] else ''
    end = ' ' if not split_string[-1] else ''

    # versus simply ' '.join(item for item in string.split(' ') if item)
    return beg + ' '.join(item for item in split_string if item) + end

original_string = """Lorem    ipsum        ... no, really, it kept going...          malesuada enim feugiat.         Integer imperdiet    erat."""

assert while_replace(original_string) == re_replace(original_string) == proper_join(original_string)

#'''

# while_replace_test
new_string = original_string[:]

new_string = while_replace(new_string)

assert new_string != original_string

# re_replace_test
new_string = original_string[:]

new_string = re_replace(new_string)

assert new_string != original_string

# proper_join_test
new_string = original_string[:]

new_string = proper_join(new_string)

assert new_string != original_string

ПРИМЕЧАНИЕ: "while версия" сделала копию original_string, как я полагаю, что после модификации при первом запуске последовательные запуски будут быстрее (если только немного). Поскольку это добавляет время, я добавил эту строковую копию к двум другим, чтобы время показало разницу только в логике. Имейте в виду, что основные stmt в timeit экземплярах будут выполняться только один раз ; Первоначально, как я это сделал, цикл while работал на той же метке, original_string, поэтому при втором запуске было бы нечего делать. То, как он настроен сейчас, вызывает функцию, используя две разные метки, это не проблема. Я добавил assert операторов ко всем работникам, чтобы убедиться, что мы что-то меняем каждую итерацию (для тех, кто может быть сомнительным). Например, измените это, и оно сломается:

# while_replace_test
new_string = original_string[:]

new_string = while_replace(new_string)

assert new_string != original_string # will break the 2nd iteration

while '  ' in original_string:
    original_string = original_string.replace('  ', ' ')

Tests run on a laptop with an i5 processor running Windows 7 (64-bit).

timeit.Timer(stmt = test, setup = setup).repeat(7, 1000)

test_string = 'The   fox jumped   over\n\t    the log.' # trivial

Python 2.7.3, 32-bit, Windows
                test |      minum |    maximum |    average |     median
---------------------+------------+------------+------------+-----------
  while_replace_test |   0.001066 |   0.001260 |   0.001128 |   0.001092
     re_replace_test |   0.003074 |   0.003941 |   0.003357 |   0.003349
    proper_join_test |   0.002783 |   0.004829 |   0.003554 |   0.003035

Python 2.7.3, 64-bit, Windows
                test |      minum |    maximum |    average |     median
---------------------+------------+------------+------------+-----------
  while_replace_test |   0.001025 |   0.001079 |   0.001052 |   0.001051
     re_replace_test |   0.003213 |   0.004512 |   0.003656 |   0.003504
    proper_join_test |   0.002760 |   0.006361 |   0.004626 |   0.004600

Python 3.2.3, 32-bit, Windows
                test |      minum |    maximum |    average |     median
---------------------+------------+------------+------------+-----------
  while_replace_test |   0.001350 |   0.002302 |   0.001639 |   0.001357
     re_replace_test |   0.006797 |   0.008107 |   0.007319 |   0.007440
    proper_join_test |   0.002863 |   0.003356 |   0.003026 |   0.002975

Python 3.3.3, 64-bit, Windows
                test |      minum |    maximum |    average |     median
---------------------+------------+------------+------------+-----------
  while_replace_test |   0.001444 |   0.001490 |   0.001460 |   0.001459
     re_replace_test |   0.011771 |   0.012598 |   0.012082 |   0.011910
    proper_join_test |   0.003741 |   0.005933 |   0.004341 |   0.004009

test_string = lorem_ipsum
# Thanks to http://www.lipsum.com/
# "Generated 11 paragraphs, 1000 words, 6665 bytes of Lorem Ipsum"

Python 2.7.3, 32-bit
                test |      minum |    maximum |    average |     median
---------------------+------------+------------+------------+-----------
  while_replace_test |   0.342602 |   0.387803 |   0.359319 |   0.356284
     re_replace_test |   0.337571 |   0.359821 |   0.348876 |   0.348006
    proper_join_test |   0.381654 |   0.395349 |   0.388304 |   0.388193    

Python 2.7.3, 64-bit
                test |      minum |    maximum |    average |     median
---------------------+------------+------------+------------+-----------
  while_replace_test |   0.227471 |   0.268340 |   0.240884 |   0.236776
     re_replace_test |   0.301516 |   0.325730 |   0.308626 |   0.307852
    proper_join_test |   0.358766 |   0.383736 |   0.370958 |   0.371866    

Python 3.2.3, 32-bit
                test |      minum |    maximum |    average |     median
---------------------+------------+------------+------------+-----------
  while_replace_test |   0.438480 |   0.463380 |   0.447953 |   0.446646
     re_replace_test |   0.463729 |   0.490947 |   0.472496 |   0.468778
    proper_join_test |   0.397022 |   0.427817 |   0.406612 |   0.402053    

Python 3.3.3, 64-bit
                test |      minum |    maximum |    average |     median
---------------------+------------+------------+------------+-----------
  while_replace_test |   0.284495 |   0.294025 |   0.288735 |   0.289153
     re_replace_test |   0.501351 |   0.525673 |   0.511347 |   0.508467
    proper_join_test |   0.422011 |   0.448736 |   0.436196 |   0.440318

Для тривиальной строки может показаться, что цикл while самый быстрый, за которым следует Pythonic string-split / join и регулярное выражение, вытягивающее заднюю часть.

Для нетривиальных строк , кажется, есть еще кое-что, что стоит рассмотреть. 32-битная 2,7? Это регулярное выражение на помощь! 2,7 64-битный? Цикл while - лучший, с приличным запасом. 32-битный 3.2, идти с "правильной" join. 64-битная 3.3, цикл while. Опять же.

В конце концов, можно улучшить производительность , если / где / когда нужно , но всегда лучше запомнить мантру :

  1. Make It Work
  2. Сделай все правильно
  3. Сделай это быстро

IANAL, YMMV, Caveat Emptor!

37 голосов
/ 10 октября 2009

Должен согласиться с комментарием Пола Макгуайра выше. Для меня

' '.join(the_string.split())

гораздо предпочтительнее, чем выводить регулярное выражение.

Мои измерения (Linux, Python 2.5) показывают, что функция split-then-join будет почти в 5 раз быстрее, чем выполнение re.sub (...), и еще в 3 раза быстрее, если вы предварительно скомпилируете регулярное выражение и сделайте операцию несколько раз. И в любом случае это легче понять - намного более питонно.

12 голосов
/ 10 октября 2009

Аналогично предыдущим решениям, но более конкретно: замените два или более пробелов одним:

>>> import re
>>> s = "The   fox jumped   over    the log."
>>> re.sub('\s{2,}', ' ', s)
'The fox jumped over the log.'
10 голосов
/ 04 ноября 2015

Простая душа

>>> import re
>>> s="The   fox jumped   over    the log."
>>> print re.sub('\s+',' ', s)
The fox jumped over the log.
5 голосов
/ 19 июня 2018

Вы также можете использовать технику разделения строк в Pandas DataFrame без необходимости использовать .apply (..), что полезно, если вам нужно быстро выполнить операцию с большим количеством строк. Вот оно в одной строке:

df['message'] = (df['message'].str.split()).str.join(' ')
3 голосов
/ 18 апреля 2017
import re
string =  re.sub('[ \t\n]+', ' ', 'The     quick brown                \n\n             \t        fox')

Это удалит все вкладки, новые строки и несколько пробелов с одним пробелом.

2 голосов
/ 25 июля 2012

Другая альтернатива

>>> import re
>>> str = 'this is a            string with    multiple spaces and    tabs'
>>> str = re.sub('[ \t]+' , ' ', str)
>>> print str
this is a string with multiple spaces and tabs
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...