有没有一种简单的方法来删除字符串中的多个空格?


390

假设此字符串:

The   fox jumped   over    the log.

转变为:

The fox jumped over the log.

什么是最简单的方法(1-2行),而无需拆分并进入列表?


22
您对列表的厌恶是什么?它们是语言的组成部分,而“” .join(list_of_words)是将字符串列表制成单个以空格分隔的字符串的核心惯用法之一。
PaulMcG,2009年

3
@ Tom / @ Paul:对于简单的字符串,(字符串)连接将是简单而甜蜜的。但是,如果还有其他不想打扰的空格,它将变得更加复杂……在这种情况下,“ while”或regex解决方案将是最佳选择。我在下面发布了一个“正确”的字符串连接,其中包含了三种测试方法的定时测试结果。
pythonlarry 2013年

Answers:


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

20
此解决方案仅处理单个空格字符。它不会替换nsr81解决方案中由\ s处理的制表符或其他空格字符。
泰勒·莱斯

2
是的,string.split还可以处理各种空白。
乔什·李

6
我更喜欢这个,因为它只关注空格字符,不会影响'\ n'之类的字符。
hhsaffar 2014年

2
是的 但是在执行strip()之前。它将消除两端的空格。
Hardik Patel

17
您可以使用re.sub(' {2,}', ' ', 'The quick brown fox')防止将single-space替换为single-space
AneesAhmed777 '18年

541

foo 是您的字符串:

" ".join(foo.split())

请注意,尽管这样做会删除“所有空白字符(空格,制表符,换行符,返回符,换页符)”(由于hhsaffar,请参见注释)。即,"this is \t a test\n"将有效地终止为"this is a test"


19
“无需拆分即可进入列表...”
Gumbo,

72
我忽略了“不拆分并进入列表...”,因为我仍然认为这是最佳答案。
泰勒·莱斯

1
这将删除尾随空格。如果要保留它们,请执行以下操作:text [0:1] +“” .join(text [1:-1] .split())+ text [-1]
user984003 2013年

也比re.sub()解决方案快6倍。
nerdfever.com

1
@ AstraUvarova-Saturn'sstar我对其进行了简介。
nerdfever.com

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

要么

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

正如使用者Martin Thoma在评论中所提到的,因为逗号前的空格在PEP 8中被列为“ 宠儿”


2
我倾向于将该正则表达式更改为,r"\s\s+"以便它不会尝试替换已经为单个的空格。
本·布兰克

19
如果您想要这种行为,为什么不只是"\s{2,}"因为不知道中等程度的正则表达式行为而采取替代方法呢?
克里斯·卢兹

2
请记住,sub()不会更改输入字符串s,而是返回新值。
gcb

1
@moose —这是一种可读性优化,而不是性能优化。 \s+将导致该行读取“用空格替换一个或多个空格”,而不是“用空格替换两个或多个空格”。前者立即让我停下来,想一想“为什么要用一个空格替换一个空格?这很愚蠢。” 对我来说,这是(非常轻微的)代码气味。我其实不认为会有任何性能差异在所有两者之间,因为它会被复制到一个新的字符串,无论如何,并且必须停止和测试,无论在空间正在被复制的
本·布兰克

8
我建议\s\s+不要这样做,因为这不会将TAB字符归一化为正常空间。SPACE + TAB确实可以通过这种方式替换。
vdboor 2015年

51

将正则表达式与“ \ s”一起使用并执行简单的string.split()也会删除其他空格,例如换行符,回车符,制表符。除非需要这样做,否则我只介绍多个示例。

我使用11个段落,1000个单词,6665字节的Lorem Ipsum进行了真实的时间测试,并在整个过程中使用了随机长度的额外空间:

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

一衬垫将基本上做任何前/后间隔的条带,并且它保留一个前/后空间(但只ONE ;-)。

# 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,因为我相信一旦在第一次运行中对其进行了修改,后续运行就会更快(如果只是一点点的话)。随着时间的增加,我将此字符串副本添加到其他两个字符串中,以便时间仅显示逻辑上的差异。 请记住,主要stmttimeit情况下,将只执行一次 ; 我执行此操作的原始方式是,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字符串拆分/连接,而regex则拉到后面。

对于非平凡的字符串,似乎还有更多需要考虑的地方。32位2.7?正则表达式可以解救!2.7 64位?一while环是最好的,通过一个体面的保证金。32位3.2,使用“ proper” join。64位3.3,进行while循环。再次。

最后,如果需要/在哪里/何时需要,人们可以提高性能,但始终最好记住这一口头禅

  1. 让它起作用
  2. 改正它
  3. 快一点

IANAL,YMMV,警告加油站!


1
如果您已经测试了简单性' '.join(the_string.split()),我会更愿意,因为这是通常的用例,但是我想对您的工作表示感谢!
wedi 2014年

@wedi:其他评论(例如来自Gumbo ; user984003的评论,尽管她/他的解决方案是推测性的,并且在所有情况下都无法使用),但这种解决方案并不符合发问者的要求。一个人可以使用.split('')和一个comp / gen,但是在处理前导/尾随空格时变得比较毛茸茸。
pythonlarry 2014年

@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 2014年

感谢@pythonlarry的口头禅!并喜欢详细的测试!我很好奇,自从6年以来,您对此的看法或看法是否有所改变?
JayRizzo

缺少版本使用发电机是

42

我必须同意保罗·麦圭尔的评论。对我来说,

' '.join(the_string.split())

比使用正则表达式要好得多。

我的测量结果(Linux和Python 2.5)显示split-then-join几乎比执行“ re.sub(...)”快五倍,如果预编译一次regex并执行操作,则快三倍。多次。它是比较容易理解的任何措施- 很多更Python。


这将删除尾随空格。如果要保留它们,请执行以下操作:text [0:1] +“” .join(text [1:-1] .split())+ text [-1]
user984003 2013年

4
一个简单的正则表达式要好得多。从未在需要之前针对性能进行优化。
gcb

@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'之类的范围内,则代码不会删除它们。
Muskovets

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

非常令人惊讶-没有人发布过简单的功能,它会比所有其他发布的解决方案快得多。它去了:

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个额外空间,并且仍然可以使用。:)


(实际上)与Anakimi的答案(发布超过两年)完全相同。
Peter Mortensen

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
这与Anakimi的答案(超过三年前发布)有何不同?不只是更复杂的版本吗?
Peter Mortensen

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删除多个空格:您可以选择以下修剪以删除空白!!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)))
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.