it-roy-ru.com

Простой способ удалить несколько пробелов в строке?

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

The   fox jumped   over    the log.

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

The fox jumped over the log.

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

272
TIMEX
>>> import re
>>> re.sub(' +', ' ', 'The     quick brown    fox')
'The quick brown fox'
370
Josh Lee

фу ваша строка:

" ".join(foo.split())

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

421
Taylor Leese
import re
s = "The   fox jumped   over    the log."
re.sub("\s\s+" , " ", s)

или же

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

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

76
Nasir

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


EDIT: Как обычно, я спал на этом, и кроме исправления опечатки в последних результатах (v3.3.3 @ 64-bit, not 32-bit), Очевидно, ударил меня: тестовая строка была довольно тривиальной.

Итак, я получил ... 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. Сделай так, чтоб это работало
  2. Сделать это правильно
  3. Сделай это быстро

IANAL, YMMV, будьте бдительны!

47
pythonlarry

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

' '.join(the_string.split())

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

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

35
Kevin Little

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

>>> import re
>>> s = "The   fox jumped   over    the log."
>>> re.sub('\s{2,}', ' ', s)
'The fox jumped over the log.'
12
Peter

Простая душа

>>> import re
>>> s="The   fox jumped   over    the log."
>>> print re.sub('\s+',' ', s)
The fox jumped over the log.
8
HMS

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

df['message'] = (df['message'].str.split()).str.join(' ')
5
devinbost
import re
string =  re.sub('[ \t\n]+', ' ', 'The     quick brown                \n\n             \t        fox')

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

3
Rakesh Kumar

В некоторых случаях желательно заменить последовательные вхождения каждого символа пробела одним экземпляром символа that . Вы бы использовали регулярное выражение с обратными ссылками, чтобы сделать это.

(\s)\1{1,} соответствует любому символу пробела, за которым следует одно или несколько вхождений этого символа. Теперь все, что вам нужно сделать, это указать первую группу (\1) в качестве замены для совпадения.

Оборачиваем это в функцию:

import re

def normalize_whitespace(string):
    return re.sub(r'(\s)\1{1,}', r'\1', string)
>>> normalize_whitespace('The   fox jumped   over    the log.')
'The fox jumped over the log.'
>>> normalize_whitespace('First    line\t\t\t \n\n\nSecond    line')
'First line\t \nSecond line'
2
vaultah

Одна строка кода для удаления всех лишних пробелов до, после и внутри предложения: 

sentence = "  The   fox jumped   over    the log.  "
sentence = ' '.join(filter(None,sentence.split(' ')))

Объяснение:

  1. Разделить всю строку в список.
  2. Фильтровать пустые элементы из списка.
  3. Воссоединить оставшиеся элементы * с одним пробелом 

* Остальные элементы должны быть словами или словами с пунктуацией и т.д. Я не проверял это подробно, но это должно стать хорошей отправной точкой. Всего наилучшего!

2
gabchan

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

>>> 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
2
Kreshnik
def unPretty(S):
   # given a dictionary, json, list, float, int, or even a string.. 
   # return a string stripped of CR, LF replaced by space, with multiple spaces reduced to one.
   return ' '.join( str(S).replace('\n',' ').replace('\r','').split() )
1
jw51

Самое быстрое, что вы можете получить для пользовательских строк:

if '  ' in text:
    while '  ' in text:
        text = text.replace('  ', ' ')

Короткое замыкание делает его немного быстрее, чем полный ответ pythonlarry . Пойдите на это, если вы стремитесь к эффективности и строго пытаетесь отсеять лишние пробелы единственного пробела.

1
Hassan Baig

Это также, кажется, работает:

while "  " in s:
    s=s.replace("  "," ")

Где переменная s представляет вашу строку.

1
Anakimi
i have tried the following method and it even works with the extreme case 
like str1='          i   live    on    earth           '

' '.join(str1.split())

but if you prefer regular expression it can be done as:-

re.sub('\s+',' ',str1)

although some preprocessing has to be done in order to remove the trailing and ending space.
0
ravi tanwar

У меня есть простой метод, который используется в колледже. 

line = "I     have            a       Nice    day."

end = 1000
while end != 0:
    line.replace("  ", " ")
    end -= 1

Это заменит каждый двойной пробел одним пробелом и сделает это 1000 раз. Это означает, что вы можете иметь 2000 дополнительных пробелов и все равно будете работать. :)

0
Zoran Bajcer

Чтобы удалить пробел, учитывая начальные, конечные и лишние пробелы между словами, используйте:

(? <=\s) + | ^ + (? =\s) | (? = + [\ n\0])

первый или имеет дело с начальным пробелом, второй или с началом строки, начинающим пробел, а последний имеет дело с конечным пробелом

для подтверждения использования эта ссылка предоставит вам тест.

https://regex101.com/r/meBYli/4

дайте мне знать, если вы найдете вход, который нарушит этот код регулярного выражения.

ТАКЖЕ - это должно использоваться с функцией re.split

0
CameronE

Если это пробел, который вы имеете дело с разделением на None, не будет включать пустую строку в возвращаемое значение.

https://docs.python.org/2/library/stdtypes.html#str.split

0
jsnklln

Я не читал много в других примерах, но я только что создал этот метод для объединения нескольких последовательных пробелов.

Он не использует никаких библиотек, и хотя он относительно длинный с точки зрения длины скрипта, он не является сложной реализацией

def spaceMatcher(command):
    """
    function defined to consolidate multiple whitespace characters in 
    strings to a single space
    """
    #initiate index to flag if more than 1 consecutive character 
    iteration
    space_match = 0
    space_char = ""
    for char in command:
      if char == " ":
          space_match += 1
          space_char += " "
      Elif (char != " ") & (space_match > 1):
          new_command = command.replace(space_char, " ")
          space_match = 0
          space_char = ""
      Elif char != " ":
          space_match = 0
          space_char = ""
   return new_command

command = None
command = str(input("Please enter a command ->"))
print(spaceMatcher(command))
print(list(spaceMatcher(command)))
0
Scott Anderson
string='This is a             string full of spaces          and taps'
string=string.split(' ')
while '' in string:
    string.remove('')
string=' '.join(string)
print(string)

Результаты :

Это строка, полная пробелов и нажатий

0
Hassan Abdul-Kareem