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

258

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

The   fox jumped   over    the log.

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

The fox jumped over the log.

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

  • 21
    Каково ваше отвращение к спискам? Они являются неотъемлемой частью языка, и "" .join (list_of_words) является одной из основных идиом для создания списка строк в одну строку, разделенную пробелом.
  • 3
    @ Tom / @ Paul: Для простых строк соединение (строка) будет простым и приятным. Но это становится более сложным, если есть другие пробелы, которые НЕ нужно беспокоить ... в этом случае решения "while" или regex будут наилучшими. Ниже я разместил строковое соединение, которое было бы «правильным», с результатами тестов по времени для трех способов сделать это.
Теги:
string

21 ответ

343
Лучший ответ
>>> import re
>>> re.sub(' +',' ','The     quick brown    fox')
'The quick brown fox'
  • 14
    Это решение обрабатывает только одиночные пробелы. Он не заменит табуляцию или другие пробельные символы, обрабатываемые \ s, как в решении nsr81.
  • 2
    Это правда, string.split также обрабатывает все виды пробелов.
Показать ещё 4 комментария
373

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

" ".join(foo.split())

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

  • 16
    «Не разбивая и не заходя в списки ...»
  • 61
    Я проигнорировал «Без разделения и перехода в списки ...», потому что я все еще думаю, что это лучший ответ.
Показать ещё 3 комментария
70
import re
s = "The   fox jumped   over    the log."
re.sub("\s\s+" , " ", s)

или

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

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

  • 2
    Я бы хотел изменить это регулярное выражение на r"\s\s+" чтобы оно не пыталось заменить уже-одиночные пробелы.
  • 1
    обновлено. Спасибо что подметил это.
Показать ещё 12 комментариев
44

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


РЕДАКТИРОВАТЬ: Как я привык делать, я спал на этом и, кроме исправления опечатки на последних результатах (v3.3.3 @64-bit, а не 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, как я полагаю, однажды измененный в первом запуске, последовательные прогоны будут быстрее (если только немного). Поскольку это добавляет время, я добавил эту строковую копию в другие две, чтобы времена показывали разницу только в логике. Имейте в виду, что main stmt on 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-loop является самым быстрым, за которым следуют Pythonic string-split/join и regex, тянущие сзади.

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

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

  • Сделать работу
  • Сделайте это правильно.
  • Сделать это быстро

IANAL, YMMV, Caveat Emptor!

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

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

' '.join(the_string.split())

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

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

  • 0
    Это удаляет завершающие пробелы. Если вы хотите сохранить их, сделайте: text [0: 1] + "" .join (text [1: -1] .split ()) + text [-1]
  • 4
    простое регулярное выражение гораздо лучше читать. никогда не оптимизируйте производительность до того, как вам это понадобится.
Показать ещё 2 комментария
12

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

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

Простая душа

>>> import re
>>> s="The   fox jumped   over    the log."
>>> print re.sub('\s+',' ', s)
The fox jumped over the log.
  • 0
    это прекрасно работает!
4

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

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

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

  • 0
    не ведущий и отставающий однако
  • 0
    Но если у вас есть пробельные символы (не для печати), которых нет в вашем диапазоне, например от \ x00 до \ x0020, код не будет их удалять.
2

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

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

Пояснение:

  • Разделить всю строку в список.
  • Отфильтровать пустые элементы из списка.
  • Присоединить оставшиеся элементы * с одним пространством

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

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
1

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

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

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

1

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

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

Это также работает:

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

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

0

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

line = "I     have            a       nice    day."

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

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

0
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

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

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

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

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

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

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

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

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

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

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

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

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню