Esiste un modo semplice per rimuovere più spazi in una stringa?


390

Supponiamo che questa stringa:

The   fox jumped   over    the log.

Trasformandosi in:

The fox jumped over the log.

Qual è il più semplice (1-2 righe) per raggiungere questo obiettivo, senza dividere e andare in elenchi?


22
Qual è la tua avversione per le liste? Sono parte integrante del linguaggio e "" .join (list_of_words) è uno dei modi di dire principali per creare un elenco di stringhe in un'unica stringa delimitata da spazi.
PaulMcG,

3
@ Tom / @ Paul: per stringhe semplici, l'unione (stringa) sarebbe semplice e dolce. Ma diventa più complesso se ci sono altri spazi bianchi che NON si desidera disturbare ... nel qual caso le soluzioni "while" o regex sarebbero le migliori. Di seguito ho pubblicato un string-join che sarebbe "corretto", con risultati dei test a tempo per tre modi per farlo.
pythonlarry,

Risposte:


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

20
Questa soluzione gestisce solo caratteri di spazio singolo. Non sostituisce una scheda o altri caratteri bianchi gestiti da \ s come nella soluzione di nsr81.
Taylor Leese,

2
È vero, string.splitgestisce anche tutti i tipi di spazi bianchi.
Josh Lee,

6
Preferisco questo perché si concentra solo sul personaggio spaziale e non influisce su personaggi come '\ n's.
hhsaffar,

2
Sì giusto. Ma prima di quella striscia () dovrebbe essere fatto. Rimuoverà gli spazi da entrambe le estremità.
Hardik Patel,

17
È possibile utilizzare re.sub(' {2,}', ' ', 'The quick brown fox')per impedire sostituzioni ridondanti di spazio singolo con spazio singolo .
AneesAhmed777,

541

foo è la tua stringa:

" ".join(foo.split())

Attenzione, questo rimuove "tutti i caratteri di spazi bianchi (spazio, tabulazione, riga nuova, ritorno, avanzamento del modulo )" (grazie a hhsaffar , vedere i commenti). Cioè, "this is \t a test\n"finirà effettivamente come "this is a test".


19
"Senza dividere e andare in liste ..."
Gumbo

72
Ho ignorato "Senza dividere e andare nelle liste ..." perché penso ancora che sia la risposta migliore.
Taylor Leese,

1
Questo rimuove gli spazi finali. Se vuoi tenerli, fallo: text [0: 1] + "" .join (text [1: -1] .split ()) + text [-1]
user984003

6 volte più veloce della soluzione re.sub ().
nerdfever.com

1
@ AstraUvarova-Saturn'sstar L'ho profilato.
nerdfever.com il

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

o

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

poiché lo spazio prima della virgola è elencato come pipì di animali domestici in PEP 8 , come indicato dall'utente Martin Thoma nei commenti.


2
Tenderei a cambiare quella regex in r"\s\s+"modo che non provi a sostituire spazi già singoli.
Ben Blank,

19
Se volevi quel comportamento, perché non solo "\s{2,}"invece di una soluzione alternativa per non conoscere un comportamento regex moderatamente avanzato?
Chris Lutz,

2
ricorda che sub () non cambia la stringa di input s, ma restituisce il nuovo valore.
gcb,

1
@moose - È un'ottimizzazione della leggibilità rispetto a una performance. \s+farebbe in modo che la riga leggesse "sostituisci uno o più spazi con uno spazio", anziché "sostituisci due o più spazi con uno spazio". Il primo mi fa immediatamente fermare e pensare "Perché sostituire uno spazio con uno spazio? È sciocco." Per me è un odore di codice (molto minore). Io in realtà non mi aspetto che ci sia alcuna differenza di prestazioni a tutti tra i due, come sta andando ad essere la copia in una nuova stringa in ogni caso, e deve fermarsi e test, a prescindere dal luogo in cui lo spazio viene copiato da .
Ben Blank,

8
Lo sconsiglio \s\s+perché questo non normalizzerà un personaggio TAB in uno spazio normale. una TABELLA SPAZIATRICE + viene sostituita in questo modo.
vdboor,

51

L'uso di regex con "\ s" e l'esecuzione di semplici string.split () rimuoverà anche altri spazi bianchi, come newline, ritorni a capo, tabulazioni. Se non lo si desidera, per fare solo più spazi , presento questi esempi.

Ho usato 11 paragrafi, 1000 parole, 6665 byte di Lorem Ipsum per ottenere prove del tempo realistiche e ho usato spazi extra di lunghezza casuale in tutto:

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

Il one-liner essenzialmente farà una striscia di tutti gli spazi iniziali / finali e conserva uno spazio iniziale / finale (ma solo UNO ;-).

# 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

NOTA: la " whileversione" ha creato una copia di original_string, come credo una volta modificata alla prima esecuzione, le successive saranno più veloci (anche se solo per un po '). Man mano che questo aggiunge tempo, ho aggiunto questa copia di stringa alle altre due in modo che i tempi mostrassero la differenza solo nella logica. Tenete a mente che il principale stmtsui timeitcasi sarà eseguito solo una volta ; nel modo originale in cui l'ho fatto, il whileloop ha funzionato sulla stessa etichetta original_string, quindi la seconda esecuzione non ci sarebbe stato nulla da fare. Il modo in cui è impostato ora, chiamando una funzione, usando due diverse etichette, non è un problema. Ho aggiunto assertdichiarazioni a tutti i lavoratori per verificare che cambiamo qualcosa ogni iterazione (per coloro che potrebbero essere dubbiosi). Ad esempio, cambia in questo e si rompe:

# 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

Per la banale stringa, sembrerebbe che un while-loop sia il più veloce, seguito da Pythonic string-split / join e regex che tira su il retro.

Per stringhe non banali , sembra che ci sia un po 'di più da considerare. 32-bit 2.7? È regex in soccorso! 2.7 a 64 bit? Un whileloop è il migliore, con un margine decente. 32-bit 3.2, andare con il "corretto" join. 64-bit 3.3, fare un whilegiro. Ancora.

Alla fine, si può migliorare le prestazioni se / dove / quando necessario , ma è sempre meglio ricordare il mantra :

  1. Fallo funzionare
  2. Fallo giusto
  3. Fallo veloce

IANAL, YMMV, Caveat Emptor!


1
Avrei preferito se avessi testato il semplice in ' '.join(the_string.split())quanto questo è il solito caso d'uso, ma vorrei ringraziarti per il tuo lavoro!
wedi,

@wedi: per altri commenti (come da Gumbo ; user984003 , anche se la sua soluzione è presuntiva e non funzionerà "in tutti i casi"), questo tipo di soluzione non aderisce alla richiesta dell'interrogatore. Si può usare .split ('') e un comp / gen, ma diventa più peloso occuparsi degli spazi di piombo / trascinamento.
pythonlarry,

@wedi: Ad esempio: ' '.join(p for p in s.split(' ') if p)<- persero ancora spazi di piombo / finali, ma rappresentavano più spazi. Per tenerli, deve fare come parts = s.split(' '); (' ' if not parts[0] else '') + ' '.join(p for p in s.split(' ') if p) + (' ' if not parts[-1] else '')!
pythonlarry,

Grazie @pythonlarry per il mantra! e adoro il test dettagliato! Sono curioso di sapere se i tuoi pensieri o opinioni sono cambiati da quando sono passati 6 anni?
JayRizzo,

Versione mancante che utilizza generatori
Lee,

42

Sono d'accordo con il commento di Paul McGuire. Per me,

' '.join(the_string.split())

è di gran lunga preferibile estrarre una regex.

Le mie misurazioni (Linux e Python 2.5) mostrano che split-then-join è quasi cinque volte più veloce rispetto a "re.sub (...)", e ancora tre volte più veloce se precompilate regex una volta e fate l'operazione più volte. Ed è comunque più facile da capire - molto più Pythonic.


Questo rimuove gli spazi finali. Se vuoi tenerli, fallo: text [0: 1] + "" .join (text [1: -1] .split ()) + text [-1]
user984003

4
è semplice leggere una regexp semplice. mai ottimizzare le prestazioni prima che sia necessario.
gcb,

@gcb: perché no? Che cosa succede se ti aspetti uno scenario di throughput elevato (ad esempio a causa della forte domanda)? Perché non distribuire qualcosa che ti aspetti di consumare meno risorse fin dall'inizio in quello scenario?
Hassan Baig

1
@HassanBaig se hai già i requisiti di prestazione, allora non è davvero un'ottimizzazione prematura, giusto? Il mio punto è quando non hai ancora bisogno di essere ossessionato dalle prestazioni, è sempre meglio puntare alla leggibilità.
gcb

14

Simile alle soluzioni precedenti, ma più specifico: sostituire due o più spazi con uno:

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

11

Un'anima semplice

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

6

Puoi anche usare la tecnica di suddivisione delle stringhe in un Pandas DataFrame senza bisogno di usare .apply (..), che è utile se devi eseguire l'operazione rapidamente su un gran numero di stringhe. Eccolo su una riga:

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

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

Ciò rimuoverà tutte le schede, le nuove linee e più spazi bianchi con un unico spazio bianco.


Ma se hai spazi bianchi (non stampabili) che non rientrano nel tuo intervallo come "\ x00" a "\ x0020" il codice non li spoglia.
Muskovets,

5

Ho provato il seguente metodo e funziona anche con il caso estremo come:

str1='          I   live    on    earth           '

' '.join(str1.split())

Ma se preferisci un'espressione regolare, puoi farlo come:

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

Anche se è necessario eseguire una preelaborazione per rimuovere lo spazio finale e finale.


3

Anche questo sembra funzionare:

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

Dove la variabile srappresenta la tua stringa.


2

In alcuni casi è desiderabile sostituire le occorrenze consecutive di ogni carattere di spazio bianco con una singola istanza di quel carattere. Per fare ciò, useresti un'espressione regolare con riferimenti indietro.

(\s)\1{1,}corrisponde a qualsiasi carattere di spazio bianco, seguito da una o più occorrenze di quel personaggio. Ora, tutto ciò che devi fare è specificare il primo gruppo ( \1) come sostituto della partita.

Avvolgendolo in una funzione:

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

Un'altra alternativa:

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

Una riga di codice per rimuovere tutti gli spazi extra prima, dopo e all'interno di una frase:

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

Spiegazione:

  1. Dividi l'intera stringa in un elenco.
  2. Filtra gli elementi vuoti dall'elenco.
  3. Ricongiungi gli elementi rimanenti * con un singolo spazio

* Gli elementi rimanenti dovrebbero essere parole o parole con punteggiatura, ecc. Non l'ho verificato ampiamente, ma questo dovrebbe essere un buon punto di partenza. Ti auguro il meglio!


2

Soluzione per sviluppatori Python:

import re

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

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

Il più veloce che puoi ottenere per le stringhe generate dall'utente è:

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

Il corto circuito lo rende leggermente più veloce della risposta completa di pythonlarry . Se vuoi l'efficienza, stai cercando di eliminare rigorosamente gli spazi bianchi della singola varietà di spazio .


1

Abbastanza sorprendente: nessuno ha pubblicato una funzione semplice che sarà molto più veloce di TUTTE le altre soluzioni pubblicate. Eccolo:

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)

Risultati :

Questa è una stringa piena di spazi e rubinetti


0

Per rimuovere lo spazio bianco, considerando le aree iniziali, finali e extra tra le parole, utilizzare:

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

Il primo orriguarda lo spazio bianco iniziale, il secondo orriguarda l'inizio dello spazio bianco iniziale della stringa e l'ultimo riguarda lo spazio bianco finale.

Per la prova dell'uso, questo link ti fornirà un test.

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

Questo deve essere usato con la funzione re.split .


0

Ho il mio metodo semplice che ho usato al college.

line = "I     have            a       nice    day."

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

Questo sostituirà ogni doppio spazio con un singolo spazio e lo farà 1000 volte. Significa che puoi avere 2000 spazi extra e funzionerà comunque. :)


Questo è (praticamente) identico alla risposta di Anakimi (pubblicata più di due anni prima).
Peter Mortensen,

0

Ho un metodo semplice senza dividere:

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
In che modo differisce dalla risposta di Anakimi (pubblicata più di tre anni prima)? Non è solo una versione più complicata?
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='') 

Risultato:

Rimuovi tutto lo spazio: puoi selezionare lo spazio in basso per rimuovere lo spazio bianco !! BRAliakbar Rimuovi lo spazio iniziale: puoi selezionare i seguenti tagli per rimuovere lo spazio bianco !! BR Aliakbar
Rimuovi gli spazi finali: puoi selezionare i seguenti ritagli per rimuovere gli spazi bianchi !! BR Aliakbar Rimuovi spazi iniziali e finali: è possibile selezionare i seguenti tagli per rimuovere gli spazi bianchi !! BR Aliakbar Rimuovi più di uno spazio: è possibile selezionare i seguenti ritagli per rimuovere gli spazi bianchi !! BR Aliakbar


-1

Non ho letto molto negli altri esempi, ma ho appena creato questo metodo per consolidare più caratteri spaziali consecutivi.

Non utilizza alcuna libreria e, sebbene sia relativamente lungo in termini di lunghezza degli script, non è un'implementazione complessa:

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)))
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.