Supposons que cette chaîne :
The fox jumped over the log.
Se transformer en :
The fox jumped over the log.
Quel est le moyen le plus simple (1 à 2 lignes) d'y parvenir, sans se diviser et entrer dans des listes ?
Supposons que cette chaîne :
The fox jumped over the log.
Se transformer en :
The fox jumped over the log.
Quel est le moyen le plus simple (1 à 2 lignes) d'y parvenir, sans se diviser et entrer dans des listes ?
Cette solution ne traite que les caractères à espace unique. Elle ne remplacerait pas une tabulation ou d'autres caractères d'espacement gérés par la fonction \s comme dans la solution de nsr81.
Je préfère celui-ci parce qu'il ne se concentre que sur le caractère espace et n'affecte pas les caractères comme '. \n 's.
foo
est votre chaîne :
" ".join(foo.split())
Attention cependant, cela supprime "tous les caractères d'espacement (espace, tabulation, nouvelle ligne, retour, formfeed)" (merci à hhsaffar voir les commentaires). C'est-à-dire, "this is \t a test\n"
se retrouvera effectivement sous la forme "this is a test"
.
J'ai ignoré "Sans diviser et sans faire de listes..." parce que je pense toujours que c'est la meilleure réponse.
Cela supprime les espaces de fin de ligne. Si vous voulez les garder, faites : texte[0:1] + " ".join(texte[1:-1].split()) + texte[-1].
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 une bête noire en PEP 8 comme mentionné par l'utilisateur Martin Thoma dans les commentaires.
J'aurais tendance à changer cette regex en r"\s\s+"
afin qu'il n'essaie pas de remplacer des espaces déjà isolés.
Si vous voulez ce comportement, pourquoi ne pas simplement "\s{2,}"
au lieu d'une solution de contournement pour ne pas connaître le comportement modérément avancé des regex ?
Utilisation de regex avec " \s "et en faisant de simples string.split(), on obtiendra également supprimer les autres espaces blancs - comme les nouvelles lignes, les retours chariot, les tabulations. À moins que cela ne soit souhaité, pour seulement faire plusieurs espaces Je vous présente ces exemples.
Utilicé 11 paragraphes, 1000 mots, 6665 octets de Lorem Ipsum. pour obtenir des tests de temps réalistes et utiliser des espaces supplémentaires de longueur aléatoire partout :
original_string = ''.join(word + (' ' * random.randint(1, 10)) for word in lorem_ipsum.split(' '))
La ligne simple supprimera essentiellement les espaces de début et de fin, et conservera un espace de début et de fin (mais seulement 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
NOTE : Le " Gardez à l'esprit que le principal while
version" a fait une copie de la original_string
Je pense qu'une fois modifié lors du premier passage, les passages suivants seront plus rapides (ne serait-ce que d'un peu). Comme cela ajoute du temps, j'ai ajouté cette copie de chaîne aux deux autres afin que les temps montrent la différence uniquement dans la logique.stmt
en timeit
Les instances ne seront exécutées qu'une seule fois ; la façon originale dont j'ai fait ça, le while
la boucle a travaillé sur le même label, original_string
Ainsi, au deuxième passage, il n'y aurait rien à faire. La façon dont il est configuré maintenant, appelant une fonction, en utilisant deux étiquettes différentes, ce n'est pas un problème. J'ai ajouté assert
à tous les travailleurs pour vérifier que nous changeons quelque chose à chaque itération (pour ceux qui seraient dubitatifs). Par exemple, changer en 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 de caractères triviale, il semblerait qu'une boucle while soit la plus rapide, suivie de la séparation/jonction de chaînes de caractères de Python, et de l'expression rationnelle.
Pour les chaînes non triviales il semble qu'il y ait un peu plus à considérer. 32-bit 2.7 ? C'est le regex qui vient à la rescousse ! 2.7 64-bit ? A while
La boucle est la meilleure, et de loin. 32-bit 3.2, allez-y avec le "bon". join
. 64-bit 3.3, allez-y pour un while
boucle. Encore une fois.
Au final, on peut améliorer les performances si/où/quand nécessaire mais c'est toujours mieux de se souvenir du mantra :
IANAL, YMMV, Caveat Emptor !
J'aurais préféré que tu testes le simple ' '.join(the_string.split())
car c'est le cas habituel, mais je tiens à vous remercier pour votre travail !
@wedi : Par d'autres commentaires (comme de Gumbo ; utilisateur984003 Bien que sa solution soit présomptueuse et ne fonctionne pas "dans tous les cas", ce type de solution ne répond pas à la demande de l'auteur de la question. On peut utiliser .split(' '), et un comp/gen, mais cela devient plus compliqué pour gérer les espaces de début et de fin.
@wedi : E.g. : ' '.join(p for p in s.split(' ') if p)
<-- on perd toujours des espaces avant/arrière, mais on tient compte de plusieurs espaces. Pour les garder, il faut faire comme parts = s.split(' '); (' ' if not parts[0] else '') + ' '.join(p for p in s.split(' ') if p) + (' ' if not parts[-1] else '')
¡!
Je suis d'accord avec le commentaire de Paul McGuire. Pour moi,
' '.join(the_string.split())
est largement préférable à l'utilisation d'une regex.
Mes mesures (Linux et Python 2.5) montrent que le split-then-join est presque cinq fois plus rapide que l'opération "re.sub(...)", et encore trois fois plus rapide si vous précompilez le regex une fois et faites l'opération plusieurs fois. Et c'est en tout cas plus facile à comprendre... beaucoup plus Python.
Cela supprime les espaces de fin de ligne. Si vous voulez les garder, faites : texte[0:1] + " ".join(texte[1:-1].split()) + texte[-1].
Une simple regexp est beaucoup plus facile à lire. n'optimisez jamais les performances avant d'en avoir besoin.
@gcb : Pourquoi pas ? Que se passe-t-il si vous prévoyez un scénario à haut débit (par exemple, en raison d'une forte demande) ? Pourquoi ne pas déployer quelque chose que vous pensez être moins gourmand en ressources dès le départ dans ce scénario ?
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
30 votes
Quelle est votre aversion pour les listes ? Elles font partie intégrante du langage, et " ".join(list_of_words) est l'un des principaux idiomes permettant de transformer une liste de chaînes de caractères en une seule chaîne délimitée par des espaces.
4 votes
@Tom/@Paul : Pour les chaînes de caractères simples, (string) join serait simple et doux. Mais cela devient plus complexe s'il y a d'autres espaces blancs que l'on ne veut PAS déranger... dans ce cas, les solutions "while" ou regex seraient les meilleures. J'ai posté ci-dessous un string-join qui serait "correct", avec des résultats de test chronométrés pour trois façons de le faire.