Bir dizede birden çok boşluğu kaldırmanın basit bir yolu var mı?


390

Bu dizeyi varsayalım:

The   fox jumped   over    the log.

Dönüşmek:

The fox jumped over the log.

Listelere ayrılmadan ve listelere girmeden bunu başarmak için en basit (1-2 satır) nedir?


22
Listelere olan istekleriniz nelerdir? Bunlar dilin ayrılmaz bir parçasıdır ve "" .join (list_of_words), dizeleri tek bir boşlukla ayrılmış dize haline getirmek için kullanılan temel deyimlerden biridir.
PaulMcG

3
@ Tom / @ Paul: Basit dizeler için, (string) birleştirme basit ve tatlı olur. Ama bir kişinin rahatsız etmek istemediği başka bir boşluk varsa daha karmaşık hale gelir ... bu durumda "while" veya regex çözümleri en iyi olurdu. Ben bunu yapmanın üç yolu için zamanlanmış test sonuçları ile, "doğru" olurdu bir dize birleştirme altında gönderdim.
pitonlarri

Yanıtlar:


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

20
Bu çözüm yalnızca tek boşluk karakterlerini işler. Nsr81'nin çözümünde olduğu gibi \ s tarafından işlenen bir sekmenin veya diğer boşluk karakterlerinin yerini almaz.
Taylor Leese

2
Bu doğrudur, string.splither türlü beyaz alanı da ele alır.
Josh Lee

6
Bunu tercih ediyorum çünkü sadece boşluk karakterine odaklanıyor ve '\ n' gibi karakterleri etkilemiyor.
hhsaffar

2
Evet doğru. Ancak bu şerit () yapılmadan önce yapılmalıdır. Her iki uçtaki boşlukları kaldıracaktır.
Hardik Patel

17
Sen kullanabilirsiniz re.sub(' {2,}', ' ', 'The quick brown fox')etmek tek boşluk tek boşluk gereksiz değiştirmeler önlemek .
AneesAhmed777

541

foo dizeniz:

" ".join(foo.split())

Bu, "tüm boşluk karakterlerini (boşluk, sekme, yeni satır, dönüş, form besleme )" kaldırdığına dikkat edin ( hhsaffar sayesinde yorumlara bakın). Yani, "this is \t a test\n"etkili bir şekilde sonuçlanacak "this is a test".


19
“Bölmeden ve listelere girmeden ...”
Gumbo

72
"Bölmeksizin ve listelere girmeden ..." ı görmezden geldim çünkü hala bunun en iyi cevap olduğunu düşünüyorum.
Taylor Leese

1
Bu, arkadaki boşlukları kaldırır. Bunları saklamak istiyorsanız: text [0: 1] + "" .join (text [1: -1] .split ()) + text [-1]
user984003 12:13

Re.sub () çözümünden 6 kat daha hızlı.
nerdfever.com

1
@ AstraUvarova-Satürn'ün yıldızı Ben profilli.
nerdfever.com

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

veya

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

virgülden önceki boşluk , kullanıcılarda Martin Thoma tarafından belirtildiği gibi , PEP 8'de bir evcil hayvan huşu olarak listelendiğinden .


2
Bu regex'i r"\s\s+"zaten tek boşlukları değiştirmeye çalışmaz.
Ben Blank

19
Bu davranışı istiyorsanız, neden yalnızca "\s{2,}"orta düzeyde gelişmiş normal ifade davranışını bilmemek için bir geçici çözüm yerine?
Chris Lutz

2
sub () öğesinin giriş dizesini değiştirmediğini s, ancak yeni değeri döndürdüğünü unutmayın.
gcb

1
@moose - Performanstan daha iyi okunabilirlik optimizasyonu. \s+satırın " iki veya daha fazla boşluğu bir boşlukla değiştir" yerine " bir veya daha fazla boşluğu boşlukla değiştir" yazmasına neden olur . İlki derhal durmamı ve "Neden bir alanı neden bir boşlukla değiştiriyorsun? Bu aptalca" diye düşünüyor. Bana göre bu çok küçük bir kod kokusu. Aslında yine yeni bir dizeye kopyalıyor gidiyor gibi ikisi arasında hiç bir performans farkı olmasını bekliyoruz olmaz ve ne olursa olsun uzay kopyalanan nereye durağı ve test vardır den .
Ben Blank

8
Bunu tavsiye ederim \s\s+çünkü bu bir SEKME karakterini normal bir alana geri döndürmez. bir SPACE + SEKME bu şekilde değiştirilir.
vdboor

51

"\ S" ile normal ifadeler kullanmak ve basit string.split () yapmak aynı zamanda yeni satırlar, satır başları, sekmeler gibi boşlukları da kaldıracaktır. Bu istenmedikçe, sadece birden çok boşluk yapmak için , bu örnekleri sunuyorum.

Kullandığım 11 paragraflar, 1000 kelime, Ipsum 6665 bayt gerçekçi zaman testleri ve kullanılan rastgele uzunlukta fazladan boşluklar boyunca almak için:

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

Tek astar esasen herhangi bir önde gelen / sondaki boşluklardan oluşan bir şerit yapar ve önde gelen / sondaki alanı korur (ancak yalnızca 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

Not: " whilesürümü" bir kopyasını yaptı, bir original_stringkez ilk çalıştırmada değiştirildiğine inanıyorum, art arda çalışır daha hızlı olurdu (eğer sadece biraz). Bu zaman ekledikçe, zamanları sadece mantıkta farkı göstermek için bu dize kopyasını diğer ikiye ekledim. Ana unutmayın stmtüzerindeki timeitdurumlarda sadece bir kez çalıştırılacaktır ; Bunu ilk yaptığım şekilde, whiledöngü aynı etiket üzerinde çalıştı original_string, bu yüzden ikinci çalışma, yapacak bir şey olmayacaktı. Şimdi ayarlanma şekli, iki farklı etiket kullanarak bir işlevi çağırmak sorun değil. assertHer bir yinelemede bir şeyi değiştirdiğimizi doğrulamak için tüm çalışanlara ifadeler ekledim (şüpheli olanlar için). Örneğin, bunu değiştirin;

# 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

Önemsiz dize için, bir while döngüsü en hızlı, ardından Pythonic string-split / join ve regex'in arkaya doğru çekildiği anlaşılıyor.

Önemsiz dizeler için dikkate alınması gereken biraz daha fazla şey var gibi görünüyor. 32-bit 2.7? Kurtarmaya düzenli! 2.7 64-bit? Bir whiledöngü en iyi, iyi bir farkla. 32-bit 3.2, "uygun" ile gidin join. 64-bit 3.3, bir whiledöngü için gidin . Tekrar.

Sonunda, gerekirse / nerede / gerektiğinde performansı artırabilir , ancak mantrayı hatırlamak her zaman en iyisidir :

  1. Çalışmasını sağla
  2. Doğru Yap
  3. Hızlı Yap

IANAL, YMMV, Uyarıcı Emptor!


1
Her ' '.join(the_string.split())zamanki kullanım durumu olduğu için basit test etseydiniz tercih ederdim ama çalışmanız için teşekkür etmek isterim!
wedi

@wedi: Diğer Yorumlar Başına (dan gibi bamya ; user984003 , ona rağmen / onun çözüm varsayımsal ve "her durumda" değil iş olacak), çözümün bu tür sorunu olanın isteğine uygun değildir. Biri .split ('') ve bir comp / gen kullanabilir, ancak kurşun / arka boşluklarla başa çıkmak için daha kıllanır.
pythonlarry

@ wedi: Örn: ' '.join(p for p in s.split(' ') if p)<- hala kayıp kurşun / sondaki boşlukları kaybettiler, ancak birden fazla boşluk muhasebeleştirdiler. Onları tutmak için, beğenmelisin parts = s.split(' '); (' ' if not parts[0] else '') + ' '.join(p for p in s.split(' ') if p) + (' ' if not parts[-1] else '')!
pythonlarry

@Pythonlarry mantra için teşekkürler! ve detaylı testi seviyorum! 6 yıldır bu konudaki düşüncelerinizin veya görüşlerinizin değişip değişmediğini merak ediyorum.
JayRizzo

Jeneratör kullanan eksik sürüm
Lee

42

Paul McGuire'ın yorumuna katılıyorum. Bana göre,

' '.join(the_string.split())

regex'i kırbaçlamak için büyük ölçüde tercih edilir.

Ölçümlerim (Linux ve Python 2.5) split-then-join öğesinin "re.sub (...)" yapmasından neredeyse beş kat daha hızlı olduğunu ve normal ifadeyi bir kez derleyip işlemi gerçekleştirdiğinizde yine de üç kat daha hızlı olduğunu gösteriyor bir kaç sefer. Ve herhangi bir önlemle anlaşılması daha kolaydır - çok daha Pythonic.


Bu, arkadaki boşlukları kaldırır. Bunları saklamak istiyorsanız: text [0: 1] + "" .join (text [1: -1] .split ()) + text [-1]
user984003 12:13

4
basit bir normal ifadeyi okumak çok daha iyidir. ihtiyaç duymadan asla performans için optimize etmeyin.
gcb

@gcb: Neden olmasın? Yüksek bir iş hacmi senaryosu bekliyorsanız (örneğin, yüksek talep nedeniyle)? Neden bu senaryoda daha az kaynak yoğun olmasını beklediğiniz bir şeyi konuşlandırmıyorsunuz?
Hassan Baig

1
@HassanBaig zaten performans gereksinimine sahipseniz, gerçekten erken optimizasyon değil, değil mi? Demek istediğim, henüz performans hakkında takıntıya ihtiyaç duymadığınızda, okunabilirliği hedeflemek her zaman daha iyidir.
gcb

14

Önceki çözümlere benzer, ancak daha spesifik: iki veya daha fazla alanı biriyle değiştirin:

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

11

Basit bir ruh

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

6

Dize bölme tekniğini bir Pandas DataFrame içinde .apply (..) kullanmanıza gerek kalmadan kullanabilirsiniz, bu da işlemi çok sayıda dizede hızlı bir şekilde yapmanız gerektiğinde yararlıdır. İşte bir satırda:

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

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

Bu, tüm sekmeleri, yeni çizgileri ve tek boşluklu birden çok beyaz boşluğu kaldıracaktır.


Ancak, aralığınızda '\ x00' ila '\ x0020' gibi boşluk (yazdırılamaz) karakterleriniz varsa, kod onları şeritlemez.
Muskovets

5

Aşağıdaki yöntemi denedim ve hatta gibi aşırı dava ile çalışır:

str1='          I   live    on    earth           '

' '.join(str1.split())

Ancak düzenli bir ifadeyi tercih ederseniz şu şekilde yapılabilir:

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

Her ne kadar sondaki ve sondaki boşluğu kaldırmak için bazı ön işlemler yapılmalıdır.


3

Bu da işe yarıyor gibi görünüyor:

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

Değişkenin sdizenizi temsil ettiği yer.


2

Bazı durumlarda, her boşluk karakterinin ardışık tekrarlarını o karakterin tek bir örneğiyle değiştirmek arzu edilir . Bunu yapmak için backreferences içeren normal bir ifade kullanırsınız.

(\s)\1{1,}boşluk karakteriyle eşleşir, ardından o karakterin bir veya daha fazla tekrarını izler. Şimdi, tek yapmanız gereken ilk grubu ( \1) eşleşmenin yerine koymaktır.

Bunu bir işlevle sarma:

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

Başka bir alternatif:

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

Bir cümle öncesi, sonrası ve içindeki tüm fazla boşlukları kaldırmak için bir kod satırı:

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

Açıklama:

  1. Dizenin tamamını bir listeye ayırın.
  2. Listeden boş öğeleri filtreleyin.
  3. Kalan öğelere * tek bir boşlukla yeniden katılın

* Kalan öğeler kelime veya noktalama işaretli kelimeler vb. Olmalıdır. Bunu kapsamlı bir şekilde test etmedim, ancak bu iyi bir başlangıç ​​noktası olmalı. Herşey gönlünce olsun!


2

Python geliştiricileri için çözüm:

import re

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

Çıktı:
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

Kullanıcı tarafından oluşturulan dizeler için en hızlısı:

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

Kısa devre, pythonlarry'nin kapsamlı cevabından biraz daha hızlı hale getirir . Verimlilik peşindeyseniz ve kesinlikle tek alan çeşitliliğinin ekstra boşluklarını ayıklamak istiyorsanız bunun için gidin .


1

Oldukça şaşırtıcı - kimse TÜM diğer yayınlanan çözümler çok daha hızlı olacak basit bir fonksiyon gönderdi. İşte gidiyor:

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)

Sonuçlar :

Bu boşluklar ve musluklar ile dolu bir dizedir


0

Kelimeler arasında aradaki boşluk, sondaki ve fazladan beyaz boşluk göz önünde bulundurarak beyaz boşluğu kaldırmak için şunu kullanın:

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

Birincisi or, önde gelen beyaz boşlukla, ikincisi, önde gelen beyaz boşlukla orbaşlar ve sonuncusu, takip eden beyaz boşlukla ilgilidir.

Kullanım kanıtı için bu bağlantı size bir test sunacaktır.

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

Bu, re.split işleviyle birlikte kullanılacaktır .


0

Üniversitede kullandığım basit yöntemim var.

line = "I     have            a       nice    day."

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

Bu, her çift boşluğu tek bir boşlukla değiştirecek ve 1000 kez yapacak. Bu, 2000 ekstra alana sahip olabileceğiniz ve yine de çalışacağınız anlamına gelir. :)


Bu (neredeyse) Anakimi'nin cevabıyla aynıdır (iki yıldan daha önce yayınlanmıştır).
Peter Mortensen

0

Bölmeden basit bir yöntem var:

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
Bu Anakimi'nin cevabından nasıl farklıdır (üç yıldan daha önce yayınlanmıştır)? Bu sadece daha karmaşık bir sürüm değil mi?
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='') 

Sonuç:

Tüm boşluğu kaldır: Beyaz boşluğu kaldırmak için aşağıdan aşağı doğru !! BRAliakbar Önde gelen boşluğu kaldır: Beyaz boşluğu kaldırmak için aşağıdaki kırpmaları seçebilirsiniz !! BR Aliakbar
Sondaki boşlukları kaldır: Beyaz boşluğu kaldırmak için aşağıdaki kaplamaları seçebilirsiniz !! BR Aliakbar Önde gelen ve arkadaki boşlukları kaldırın: Beyaz alanı kaldırmak için aşağıdaki kaplamaları seçebilirsiniz !! BR Aliakbar Birden fazla alanı kaldırma: Beyaz alanı kaldırmak için aşağıdaki kenarları seçebilirsiniz !! BR Aliakbar


-1

Diğer örneklere çok fazla şey okumadım, ancak birkaç ardışık boşluk karakterini birleştirmek için bu yöntemi oluşturdum.

Herhangi bir kütüphane kullanmaz ve kod uzunluğu açısından nispeten uzun olsa da, karmaşık bir uygulama değildir:

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)))
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.