web-dev-qa-db-fra.com

Un moyen simple de supprimer plusieurs espaces dans une chaîne?

Supposons que ceci soit la chaîne:

The   fox jumped   over    the log.

Il en résulterait:

The fox jumped over the log.

Quel est le plus simple, une ou deux doublures pouvant le faire? Sans se séparer et entrer dans les listes ...

272
TIMEX
>>> import re
>>> re.sub(' +', ' ', 'The     quick brown    fox')
'The quick brown fox'
370
Josh Lee

foo est votre ficelle:

" ".join(foo.split())

Soyez averti si cela supprime "tous les caractères d'espacement (espace, tabulation, nouvelle ligne, retour, saut de page)". (Merci à hhsaffar , voir les commentaires) ie "this is \t a test\n" finira par devenir "this is a test"

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

ou

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

puisque l'espace avant la virgule est répertorié comme un bête de bête dans PEP8, comme indiqué par orignal dans les commentaires.

76
Nasir

En utilisant des expressions rationnelles avec "\ s" et en effectuant une simple chaîne, string.split () va aussi supprimer les autres espaces, comme les nouvelles lignes, les retours à la ligne, les tabulations. À moins que cela ne soit souhaité, pour uniquement faire plusieurs espaces, je présente ces exemples.


EDIT: Comme d'habitude, j'ai dormi là-dessus et, en plus de corriger une faute de frappe sur les derniers résultats (v3.3.3 @ 64 bits, pas 32 bits), Une évidence me frappe: la chaîne de test était plutôt triviale.

J'ai donc ... 11 paragraphes, 1000 mots, 6665 octets de Lorem Ipsum pour obtenir des tests de temps plus réalistes. J'ai ensuite ajouté des espaces supplémentaires de longueur aléatoire:

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

J'ai également corrigé la "bonne join"; si on s'en soucie, one-liner fera essentiellement une bande d'espaces de début/fin, cette version corrigée conserve un espace de début/fin (mais seulement UN ;-). (J'ai trouvé cela parce que le lorem_ipsum à espacement aléatoire avait des espaces supplémentaires à la fin et qu'il échouait donc la 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

REMARQUE: La "version while" a fait une copie du original_string. Comme je le crois, une fois modifiée lors de la première exécution, les exécutions successives seraient plus rapides (ne serait-ce que de peu). Comme cela ajoute du temps, j'ai ajouté cette copie de chaîne aux deux autres pour que les temps ne montrent la différence que dans la logique. N'oubliez pas que les instances stmt sur timeit ne seront exécutées qu'une seule fois ; De la même manière que j’ai fait cela, la boucle while a fonctionné sur la même étiquette, original_string, de sorte que lors de la deuxième exécution, il n’y aurait rien à faire. La configuration actuelle, appeler une fonction, utiliser deux étiquettes différentes, n’est pas un problème. J'ai ajouté des instructions assert à tous les travailleurs pour vérifier que nous modifions quelque chose à chaque itération (pour ceux qui peuvent être douteux). Par exemple, changez ceci et ça casse:

# 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

Pour la chaîne triviale, il semblerait que la boucle while soit la plus rapide, suivie de la division/jointure de chaînes Pythonic et de la regex tirant vers le haut.

Pour les chaînes non triviales, il semble y avoir un peu plus à considérer. 32 bits 2.7? C'est regex à la rescousse! 2.7 64 bits? Une boucle while est préférable, par une marge décente. 32 bits 3.2, allez avec la "bonne" join. 64 bits 3.3, optez pour une boucle while. Encore.

Au final, on peut améliorer les performances si/où/quand cela est nécessaire, mais il est toujours préférable de rappeler le mantra :

  1. Fais-le fonctionner
  2. Fais-le bien
  3. Fais vite

IANAL, YMMV, Caveat Emptor!

47
pythonlarry

Je suis d'accord avec le commentaire de Paul McGuire ci-dessus. Pour moi, 

' '.join(the_string.split())

est nettement préférable à sortir une regex. 

Mes mesures (Linux, Python 2.5) montrent que le split-then-join est presque 5 fois plus rapide que de faire "re.sub (...)", et toujours 3 fois plus rapidement si vous précompilez la regex une fois et effectuez l'opération plusieurs fois. Et il est de toute façon plus facile de comprendre - beaucoup plus Pythonic.

35
Kevin Little

Semblable aux solutions précédentes, mais plus spécifique: remplacez deux espaces ou plus par un:

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

Une simple soultion

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

Vous pouvez également utiliser la technique de fractionnement de chaîne dans un fichier de données Pandas sans avoir besoin d'utiliser .apply (..), ce qui est utile si vous devez exécuter l'opération rapidement sur un grand nombre de chaînes. La voici sur une seule ligne:

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

Cela supprimera tout l’onglet, les nouvelles lignes et plusieurs espaces blancs avec un seul espace blanc.

3
Rakesh Kumar

Dans certains cas, il est souhaitable de remplacer les occurrences consécutives de chaque caractère d'espacement par une seule instance de that character. Vous utiliseriez une expression régulière avec des références arrières pour le faire.

(\s)\1{1,} correspond à n'importe quel caractère d'espacement, suivi d'une ou plusieurs occurrences de ce caractère. Maintenant, tout ce que vous avez à faire est de spécifier le premier groupe (\1) en remplacement de la correspondance.

Envelopper ceci dans une fonction:

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
vaultah

Une ligne de code pour supprimer tous les espaces supplémentaires avant, après et dans une phrase: 

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

Explication:

  1. Fractionner la chaîne entière dans la liste.
  2. Filtrer les éléments vides de la liste.
  3. Rejoignez les éléments restants * avec un seul espace 

* Les éléments restants doivent être des mots ou des mots avec des ponctuations, etc. Je n'ai pas testé cela de manière approfondie, mais cela devrait être un bon point de départ. Bonne chance!

2
gabchan

Autre alternative

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

Le plus rapidement possible pour les chaînes générées par l'utilisateur est:

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

Le court-circuit le rend légèrement plus rapide que la réponse complète de pythonlarry . Optez pour cela si vous recherchez l'efficacité et que vous cherchez strictement à éliminer les espaces blancs supplémentaires de la variété d'espace unique.

1
Hassan Baig

Cela semble aussi fonctionner:

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

Où la variable s représente votre chaîne.

1
Anakimi
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
ravi tanwar

J'ai ma méthode simple qui ont utilisé à l'université. 

line = "I     have            a       Nice    day."

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

Cela remplacera chaque double espace par un seul espace et le fera 1000 fois. Cela signifie que vous pouvez avoir 2000 espaces supplémentaires et que vous travaillerez toujours. :)

0
Zoran Bajcer

Pour supprimer les espaces, en considérant les espaces de début, de fin et extra entre les mots, utilisez:

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

le premier ou traite des espaces blancs de premier plan, le second ou du début des espaces blancs de début de chaîne, et le dernier concerne les espaces blancs de fin

comme preuve d'utilisation, ce lien vous fournira un test.

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

laissez-moi savoir si vous trouvez une entrée qui rompra ce code regex.

AUSSI - à utiliser avec la fonction re.split

0
CameronE

S'il s'agit d'espaces, le fractionnement sur Aucun n'inclut pas de chaîne vide dans la valeur renvoyée.

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

0
jsnklln

Je n'ai pas beaucoup lu dans les autres exemples, mais je viens de créer cette méthode pour consolider plusieurs espaces consécutifs.

Il n'utilise aucune bibliothèque et, même s'il est relativement long en termes de longueur de script, sa mise en œuvre n'est pas complexe.

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
Scott Anderson
string='This is a             string full of spaces          and taps'
string=string.split(' ')
while '' in string:
    string.remove('')
string=' '.join(string)
print(string)

résultats :

Ceci est une chaîne pleine d'espaces et de robinets

0
Hassan Abdul-Kareem