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


390

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

The   fox jumped   over    the log.

Превращаться в:

The fox jumped over the log.

Что проще всего (1-2 строки) добиться этого, не разбивая и не заходя в списки?


22
Каково ваше отвращение к спискам? Они являются неотъемлемой частью языка, и "" .join (list_of_words) является одной из основных идиом для создания списка строк в одну строку, разделенную пробелом.
PaulMcG

3
@ Tom / @ Paul: Для простых строк соединение (строка) будет простым и приятным. Но это становится более сложным, если есть другие пробелы, которые НЕ НАДО беспокоить ... в этом случае решения "while" или regex будут наилучшими. Ниже я разместил строковое соединение, которое было бы «правильным», с результатами тестов по времени для трех способов сделать это.
pythonlarry

Ответы:


529
>>> import re
>>> re.sub(' +', ' ', 'The     quick brown    fox')
'The quick brown fox'

20
Это решение обрабатывает только одиночные пробелы. Он не заменит табуляцию или другие пробельные символы, обрабатываемые \ s, как в решении nsr81.
Тейлор Лиз

2
Это правда, string.splitтакже обрабатывает все виды пробелов.
Джош Ли

6
Я предпочитаю этот, потому что он фокусируется только на символе пробела и не влияет на символы типа '\ n's.
hhsaffar

2
Да, верно. Но перед этим strip () должно быть сделано. Это удалит пробелы с обоих концов.
Хардик Патель

17
Вы можете использовать re.sub(' {2,}', ' ', 'The quick brown fox')для предотвращения избыточной замены одного пробела на один пробел .
AneesAhmed777

541

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

" ".join(foo.split())

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


19
«Без разбиения и перехода в списки ...»
Гамбо

72
Я проигнорировал «Без разделения и перехода в списки ...», потому что я все еще думаю, что это лучший ответ.
Тейлор Лиз

1
Это удаляет завершающие пробелы. Если вы хотите сохранить их, сделайте: text [0: 1] + "" .join (text [1: -1] .split ()) + text [-1]
user984003

В 6 раз быстрее, чем решение re.sub ().
nerdfever.com

1
@ AstraUvarova-Сатурнская звезда, я ее профилировал.
nerdfever.com

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

или

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

так как пробел перед запятой указан как любимая мозоль в PEP 8 , как упомянул пользователь Martin Thoma в комментариях.


2
Я бы предпочел изменить это регулярное выражение, чтобы r"\s\s+"оно не пыталось заменить уже одиночные пробелы.
Бен Бланк

19
Если вы хотели такое поведение, то почему бы просто "\s{2,}"вместо обходного пути не знать умеренно продвинутое поведение регулярных выражений?
Крис Латс

2
помните, что sub () не изменяет входную строку s, но возвращает новое значение.
gcb

1
@moose - это оптимизация читабельности, а не производительность. \s+вызовет в строке «заменить один или несколько пробелов пробелом», а не «заменить два или более пробелов пробелом». Первое немедленно заставляет меня остановиться и подумать: «Зачем заменять один пробел одним пробелом? Это глупо». Для меня это (очень незначительный) запах кода. Я на самом деле не ожидаю , что любая разница в производительности вообще между ними, как это будет копировать в новую строку , в любом случае, и должен остановиться и испытание , независимо от того, где пространства копируются из .
Бен Бланк

8
Я бы посоветовал против, \s\s+потому что это не нормализует символ табуляции обратно в обычный пробел. SPACE + TAB действительно заменяется таким образом.
vdboor

51

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

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

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

Однострочник по существу делает полосу любых начальных / конечных пробелов, и он сохраняет начальную / конечную пробелы (но только ОДИН ;-).

# 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, как я полагаю, однажды измененную при первом запуске, последовательные запуски будут быстрее (хотя бы немного). Поскольку это добавляет время, я добавил эту строковую копию к двум другим, чтобы время показало разницу только в логике. Имейте в виду, что main 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, будьте бдительны!


1
Я бы предпочел, чтобы вы протестировали простое, так ' '.join(the_string.split())как это обычный вариант использования, но я хотел бы поблагодарить вас за вашу работу!
WEDI

@wedi: По другим комментариям (например, от Gumbo ; user984003 , хотя ее / ее решение является предположительным и не будет работать «во всех случаях»), такое решение не соответствует запросу спрашивающего. Кто-то может использовать .split ('') и comp / gen, но получает больше удовольствия, чтобы иметь дело с пробелами в конце и в конце.
pythonlarry

@wedi: Например: ' '.join(p for p in s.split(' ') if p)<- все еще потерял пробелы вперед / назад, но учитывал несколько пробелов. Чтобы их сохранить, надо делать как parts = s.split(' '); (' ' if not parts[0] else '') + ' '.join(p for p in s.split(' ') if p) + (' ' if not parts[-1] else '')!
pythonlarry

Спасибо @pythonlarry за мантру! и люблю подробный тест! Мне любопытно узнать, изменились ли ваши мысли или взгляды на это с 6 лет?
JayRizzo

Отсутствует версия, в которой используются генераторы
Ли

42

Я должен согласиться с комментарием Пола Макгуайра. Мне,

' '.join(the_string.split())

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

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


Это удаляет завершающие пробелы. Если вы хотите сохранить их, выполните: text [0: 1] + "" .join (text [1: -1] .split ()) + text [-1]
user984003

4
простое регулярное выражение гораздо лучше читать. никогда не оптимизируйте производительность до того, как вам это понадобится.
ГКБ

@gcb: почему бы и нет? Что, если вы ожидаете сценарий с высокой пропускной способностью (например, из-за высокого спроса)? Почему бы не развернуть то, что, как вы ожидаете, будет менее ресурсоемким с самого начала в этом сценарии?
Хасан Бэйг

1
@HassanBaig Если у вас уже есть требования к производительности, то это не слишком преждевременная оптимизация, верно? Я хочу сказать, что когда вам еще не нужно думать о производительности, всегда лучше стремиться к удобочитаемости.
gcb

14

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

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

11

Простая душа

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

6

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

df['message'] = (df['message'].str.split()).str.join(' ')

6
import re
string = re.sub('[ \t\n]+', ' ', 'The     quick brown                \n\n             \t        fox')

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


Но если у вас есть пробельные символы (не для печати), которых нет в вашем диапазоне, например от \ x00 до \ x0020, код не будет их удалять.
Московец

5

Я попробовал следующий метод, и он даже работает в крайнем случае, как:

str1='          I   live    on    earth           '

' '.join(str1.split())

Но если вы предпочитаете регулярное выражение, это можно сделать так:

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

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


3

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

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

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


2

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

(\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

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

>>> 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

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

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

Объяснение:

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

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


2

Решение для разработчиков Python:

import re

text1 = 'Python      Exercises    Are   Challenging Exercises'
print("Original string: ", text1)
print("Without extra spaces: ", re.sub(' +', ' ', text1))

Вывод:
Original string: Python Exercises Are Challenging Exercises Without extra spaces: Python Exercises Are Challenging Exercises


1
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

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

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

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


1

Удивительно - никто не опубликовал простую функцию, которая будет намного быстрее, чем ВСЕ другие опубликованные решения. Здесь это идет:

def compactSpaces(s):
    os = ""
    for c in s:
        if c != " " or os[-1] != " ":
            os += c 
    return os


0
string = 'This is a             string full of spaces          and taps'
string = string.split(' ')
while '' in string:
    string.remove('')
string = ' '.join(string)
print(string)

Результаты :

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


0

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

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

Первый orимеет orдело с начальными пробелами, второй - с начальными строками, а последний - с пробелами.

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

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

Это должно использоваться с функцией re.split .


0

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

line = "I     have            a       nice    day."

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

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


Это (практически) идентично ответу Анакими (опубликованному более двух лет назад).
Питер Мортенсен

0

У меня есть простой метод без разделения:

a = "Lorem   Ipsum Darum     Diesrum!"
while True:
    count = a.find("  ")
    if count > 0:
        a = a.replace("  ", " ")
        count = a.find("  ")
        continue
    else:
        break

print(a)

1
Чем это отличается от ответа Анакими (опубликованного более трех лет назад)? Разве это не более сложная версия?
Питер Мортенсен

0
import re

Text = " You can select below trims for removing white space!!   BR Aliakbar     "
  # trims all white spaces
print('Remove all space:',re.sub(r"\s+", "", Text), sep='') 
# trims left space
print('Remove leading space:', re.sub(r"^\s+", "", Text), sep='') 
# trims right space
print('Remove trailing spaces:', re.sub(r"\s+$", "", Text), sep='')  
# trims both
print('Remove leading and trailing spaces:', re.sub(r"^\s+|\s+$", "", Text), sep='')
# replace more than one white space in the string with one white space
print('Remove more than one space:',re.sub(' +', ' ',Text), sep='') 

Результат:

Удалить все пробелы: вы можете выбрать нижеприведенные поля для удаления пробелов !! BRAliakbar Удалить начальные пробелы: вы можете выбрать следующие значения для удаления пробелов !! BR Aliakbar
Удалить завершающие пробелы: вы можете выбрать следующие значения для удаления пробелов !! BR Aliakbar Удалить начальные и конечные пробелы: вы можете выбрать следующие ограничения для удаления пробелов !! BR Aliakbar Удалить более одного пробела: Вы можете выбрать ниже трим для удаления пустого пространства! БР Алиакбар


-1

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

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

def spaceMatcher(command):
    """
    Function defined to consolidate multiple whitespace characters in
    strings to a single space
    """
    # Initiate index to flag if more than one 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)))
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.