718 Stimmen

Gibt es eine einfache Möglichkeit, mehrere Leerzeichen in einer Zeichenkette zu entfernen?

Nehmen wir diese Zeichenfolge an:

The   fox jumped   over    the log.

Verwandlung in:

The fox jumped over the log.

Was ist die einfachste (1-2 Zeilen), um dies zu erreichen, ohne zu spalten und in Listen zu gehen?

31 Stimmen

Woher kommt Ihre Abneigung gegen Listen? Sie sind ein integraler Bestandteil der Sprache, und " ".join(list_of_words) ist eines der wichtigsten Idiome, um eine Liste von Zeichenketten in eine einzige, durch Leerzeichen getrennte Zeichenkette zu verwandeln.

5 Stimmen

@Tom/@Paul: Für einfache Strings wäre (string) join einfach und nett. Aber es wird komplexer, wenn es andere Leerzeichen gibt, die man NICHT stören will... in diesem Fall wären "while" oder Regex-Lösungen am besten. Nachfolgend habe ich eine "korrekte" String-Verknüpfung gepostet, mit zeitlich begrenzten Testergebnissen für drei Möglichkeiten, dies zu tun.

927voto

Josh Lee Punkte 159535
>>> import re
>>> re.sub(' +', ' ', 'The     quick brown    fox')
'The quick brown fox'

859voto

Taylor Leese Punkte 48438

foo ist Ihre Zeichenkette:

" ".join(foo.split())

Seien Sie jedoch gewarnt, dass dadurch "alle Leerzeichen (Leerzeichen, Tabulator, Zeilenumbruch, Return, Formfeed)" entfernt werden (Dank an hhsaffar (siehe Kommentare). D.h., "this is \t a test\n" wird effektiv enden als "this is a test" .

138voto

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

o

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

da das Leerzeichen vor dem Komma als Lieblingsärgernis in PEP 8 als vom Benutzer erwähnt Martin Thoma in den Kommentaren.

65voto

pythonlarry Punkte 2126

Verwendung von Regexen mit " \s " und einfache string.split()'s werden auch andere Leerzeichen - wie Zeilenumbrüche, Absatzende, Tabulatoren - entfernen. Wenn dies nicht erwünscht ist, sollten Sie nur faire mehrere Räume stelle ich diese Beispiele vor.

Ich habe 11 Absätze, 1000 Wörter, 6665 Bytes Lorem Ipsum um realistische Zeittests zu erhalten, und verwendete durchgehend zusätzliche Leerzeichen von zufälliger Länge:

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

Der Einzeiler entfernt im Wesentlichen alle führenden/nachfolgenden Leerzeichen und behält ein führendes/nachfolgendes Leerzeichen bei (aber nur 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

HINWEIS: Die " while Version" hat eine Kopie der original_string denn ich glaube, dass nach einer Änderung beim ersten Durchlauf die folgenden Durchläufe schneller werden (wenn auch nur um ein bisschen). Da dies Zeit kostet, habe ich diese Kopie der Zeichenkette zu den beiden anderen hinzugefügt, so dass die Zeiten nur den Unterschied in der Logik zeigen. Denken Sie daran, dass die wichtigsten stmt auf timeit Instanzen werden nur einmal ausgeführt Die ursprüngliche Methode, mit der ich das gemacht habe, war die while Schleife an demselben Label gearbeitet, original_string Beim zweiten Durchgang gäbe es also nichts mehr zu tun. So wie es jetzt eingerichtet ist, eine Funktion aufzurufen, die zwei verschiedene Bezeichnungen verwendet, ist das kein Problem. Ich habe hinzugefügt assert Anweisungen an alle Arbeiter, um zu überprüfen, ob wir bei jeder Iteration etwas ändern (für diejenigen, die vielleicht Zweifel haben). Z.B., ändere dies und es bricht ab:

# 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

Für die triviale Zeichenkette scheint eine while-Schleife am schnellsten zu sein, gefolgt von Pythonic string-split/join und regex, das das Schlusslicht bildet.

Für nicht-triviale Zeichenketten scheint es, als gäbe es noch etwas mehr zu bedenken. 32-Bit 2.7? Regex ist die Rettung! 2.7 64-Bit? A while Schleife ist mit Abstand am besten. 32-Bit 3.2, gehen Sie mit dem "richtigen" join . 64-bit 3.3, wählen Sie eine while Schleife. Nochmals.

Letztendlich kann man die Leistung verbessern falls/wo/wann erforderlich aber es ist immer am besten, wenn erinnere dich an das Mantra :

  1. Damit es klappt
  2. Machen Sie es richtig
  3. Schnell machen

IANAL, YMMV, Caveat Emptor!

61voto

Kevin Little Punkte 11796

Ich muss dem Kommentar von Paul McGuire zustimmen. Für mich,

' '.join(the_string.split())

ist der Verwendung einer Regex bei weitem vorzuziehen.

Meine Messungen (Linux und Python 2.5) zeigen, dass Split-then-Join fast fünfmal schneller ist als "re.sub(...)", und immer noch dreimal schneller, wenn man die Regex einmal vorkompiliert und die Operation mehrfach durchführt. Und es ist auf jeden Fall einfacher zu verstehen -- viel mehr pythonisch.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X