Apakah ada cara sederhana untuk menghapus beberapa spasi dalam sebuah string?


390

Misalkan string ini:

The   fox jumped   over    the log.

Berubah menjadi:

The fox jumped over the log.

Apa yang paling sederhana (1-2 baris) untuk mencapai ini, tanpa membelah dan masuk ke daftar?


22
Apa keengganan Anda untuk mendaftar? Mereka adalah bagian integral dari bahasa, dan "" .join (list_of_words) adalah salah satu idiom inti untuk membuat daftar string menjadi string yang dibatasi ruang.
PaulMcG

3
@ Tom / @ Paul: Untuk string sederhana, (string) bergabung akan menjadi sederhana dan manis. Tapi itu menjadi lebih kompleks jika ada spasi putih lain yang TIDAK ingin diganggu ... dalam hal ini "sementara" atau solusi regex akan menjadi yang terbaik. Saya telah memposting string-join yang akan "benar", dengan hasil tes waktunya untuk tiga cara melakukan hal ini.
pythonlarry

Jawaban:


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

20
Solusi ini hanya menangani karakter spasi tunggal. Itu tidak akan menggantikan tab atau karakter spasi putih lainnya yang ditangani oleh seperti dalam solusi nsr81.
Taylor Leese

2
Itu benar, string.splitjuga menangani semua jenis ruang putih.
Josh Lee

6
Saya lebih suka yang ini karena hanya berfokus pada karakter spasi dan tidak memengaruhi karakter seperti '\ n's.
hhsaffar

2
Ya benar. Tetapi sebelum strip itu () harus dilakukan. Ini akan menghapus spasi dari kedua ujungnya.
Hardik Patel

17
Anda dapat menggunakan re.sub(' {2,}', ' ', 'The quick brown fox')untuk mencegah penggantian ruang tunggal dengan ruang tunggal berlebih .
AneesAhmed777

541

foo adalah string Anda:

" ".join(foo.split())

Berhati-hatilah meskipun ini menghapus "semua karakter spasi putih (spasi, tab, baris baru, kembali, formfeed)" (terima kasih kepada hhsaffar , lihat komentar). Yaitu, "this is \t a test\n"secara efektif akan berakhir sebagai "this is a test".


19
"Tanpa membelah dan masuk ke daftar ..."
Gumbo

72
Saya mengabaikan "Tanpa membelah dan masuk ke daftar ..." karena saya masih berpikir itu adalah jawaban terbaik.
Taylor Leese

1
Ini menghilangkan spasi tambahan. Jika Anda ingin tetap melakukannya: text [0: 1] + "" .join (text [1: -1] .split ()) + text [-1]
user984003

6x lebih cepat dari solusi re.sub () juga.
nerdfever.com

1
@ AstraUvarova-Saturn'sstar I membuat profilnya.
nerdfever.com

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

atau

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

karena spasi sebelum koma terdaftar sebagai pet peeve di PEP 8 , sebagaimana disebutkan oleh pengguna Martin Thoma dalam komentar.


2
Saya cenderung mengubah regex itu r"\s\s+"sehingga tidak mencoba mengganti spasi yang sudah tunggal.
Ben Blank

19
Jika Anda menginginkan perilaku itu, mengapa tidak sekadar "\s{2,}"solusi untuk tidak mengetahui perilaku regex yang cukup canggih?
Chris Lutz

2
ingat bahwa sub () tidak mengubah string input s, tetapi mengembalikan nilai baru.
gcb

1
@moose - Ini adalah pengoptimalan keterbacaan dari pada kinerja. \s+akan menyebabkan baris membaca "ganti satu atau lebih spasi dengan spasi", daripada "ganti dua atau lebih spasi dengan spasi". Yang pertama segera membuat saya berhenti dan berpikir, "Mengapa mengganti satu ruang dengan satu ruang? Itu konyol." Bagi saya, itu bau kode (sangat kecil). Aku benar-benar tidak akan berharap ada menjadi perbedaan kinerja sama sekali antara dua, karena itu akan menyalin ke dalam sebuah string baru pula, dan harus berhenti dan uji terlepas dari di mana ruang yang disalin dari .
Ben Blank

8
Saya akan menyarankan \s\s+karena ini tidak akan menormalkan karakter TAB kembali ke ruang normal. SPACE + TAB diganti dengan cara ini.
vdboor

51

Menggunakan regex dengan "\" dan melakukan string.split () yang sederhana juga akan menghapus spasi putih lainnya - seperti baris baru, carriage return, tab. Kecuali jika ini diinginkan, untuk hanya melakukan beberapa spasi , saya menyajikan contoh-contoh ini.

Saya menggunakan 11 paragraf, 1000 kata, 6665 byte Lorem Ipsum untuk mendapatkan tes waktu yang realistis dan menggunakan ruang ekstra panjang acak di seluruh:

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

One-liner pada dasarnya akan melakukan strip dari setiap ruang terdepan / tambahan, dan mempertahankan ruang terdepan / tertinggal (tetapi hanya SATU ;-).

# 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

CATATAN: " whileVersi" membuat salinan original_string, karena saya percaya sekali dimodifikasi pada jalankan pertama, berjalan berturut-turut akan lebih cepat (jika hanya sedikit). Karena ini menambah waktu, saya menambahkan salinan string ini ke dua lainnya sehingga waktu menunjukkan perbedaan hanya dalam logika. Perlu diingat bahwa utama stmtpada timeitkasus hanya akan dieksekusi sekali ; cara asli saya melakukan ini, whileloop bekerja pada label yang sama original_string, sehingga menjalankan kedua, tidak ada yang bisa dilakukan. Cara pengaturannya sekarang, memanggil fungsi, menggunakan dua label berbeda, itu tidak masalah. Saya telah menambahkan assertpernyataan kepada semua pekerja untuk memverifikasi bahwa kami mengubah sesuatu setiap iterasi (bagi mereka yang mungkin ragu). Misalnya, ubah ke ini dan rusak:

# 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

Untuk string sepele, akan terlihat bahwa loop sementara adalah yang tercepat, diikuti oleh string-split / join Pythonic, dan regex menarik ke belakang.

Untuk string non-sepele , sepertinya ada sedikit lebih untuk dipertimbangkan. 32-bit 2,7? Ini regex untuk menyelamatkan! 2,7 64-bit? Sebuah whilelingkaran yang terbaik, dengan margin yang layak. 32-bit 3.2, pergi dengan "tepat" join. 64-bit 3.3, lakukan whileperulangan. Lagi.

Pada akhirnya, seseorang dapat meningkatkan kinerja jika / di mana / kapan dibutuhkan , tetapi yang terbaik adalah mengingat mantra :

  1. Buat itu bekerja
  2. Perbaiki itu
  3. Buatlah Cepat

IANAL, YMMV, Caveat Emptor!


1
Saya lebih suka jika Anda telah menguji yang sederhana ' '.join(the_string.split())karena ini adalah kasus penggunaan biasa tetapi saya ingin mengucapkan terima kasih atas pekerjaan Anda!
wedi

@wedi: Per komentar lain (seperti dari Gumbo ; user984003 , meskipun solusinya adalah dugaan dan tidak akan bekerja "dalam semua kasus"), solusi semacam ini tidak mematuhi permintaan si penanya. Seseorang dapat menggunakan .split (''), dan sebuah comp / gen, tetapi mendapat hairier untuk berurusan dengan spasi lead / trailing.
pythonlarry

@wedi: Misalnya: ' '.join(p for p in s.split(' ') if p)<- masih kehilangan spasi awal / akhir, tetapi menyumbang beberapa ruang. Untuk menjaganya, pasti suka parts = s.split(' '); (' ' if not parts[0] else '') + ' '.join(p for p in s.split(' ') if p) + (' ' if not parts[-1] else '')!
pythonlarry

Terima kasih @pythonlarry atas mantranya! dan suka tes terperinci! Saya ingin tahu apakah pemikiran atau pandangan Anda telah berubah sejak 6 tahun ini?
JayRizzo

Versi hilang yang menggunakan generator
Lee

42

Saya harus setuju dengan komentar Paul McGuire. Untuk saya,

' '.join(the_string.split())

jauh lebih baik daripada mencabut regex.

Pengukuran saya (Linux dan Python 2.5) menunjukkan split-then-join menjadi hampir lima kali lebih cepat daripada melakukan "re.sub (...)", dan masih tiga kali lebih cepat jika Anda mengkompilasi ulang regex sekali dan melakukan operasi beberapa kali. Dan dengan cara apa pun lebih mudah dipahami - jauh lebih Pythonic.


Ini menghilangkan spasi tambahan. Jika Anda ingin tetap melakukannya: text [0: 1] + "" .join (text [1: -1] .split ()) + text [-1]
user984003

4
regexp sederhana jauh lebih baik untuk dibaca. jangan pernah mengoptimalkan kinerja sebelum Anda perlu.
gcb

@ gcb: Kenapa tidak? Bagaimana jika Anda mengharapkan skenario throughput yang tinggi (misalnya karena permintaan tinggi)? Mengapa tidak menggunakan sesuatu yang Anda harapkan menjadi kurang intensif sumber daya dari skenario dalam skenario itu?
Hassan Baig

1
@HassanBaig jika Anda sudah memiliki persyaratan kinerja, maka itu bukan optimasi prematur, bukan? Maksud saya adalah ketika Anda tidak perlu terobsesi dengan kinerja, selalu lebih baik bertujuan untuk keterbacaan.
gcb

14

Mirip dengan solusi sebelumnya, tetapi lebih spesifik: ganti dua atau lebih spasi dengan satu:

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

11

Sebuah soultion sederhana

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

6

Anda juga dapat menggunakan teknik pemisahan string dalam Pandaf DataFrame tanpa perlu menggunakan .apply (..), yang berguna jika Anda perlu melakukan operasi dengan cepat pada sejumlah besar string. Ini dia dalam satu baris:

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

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

Ini akan menghapus semua tab, garis baru dan banyak spasi putih dengan spasi putih tunggal.


Tetapi jika Anda memiliki karakter spasi (tidak dapat dicetak) yang tidak dalam jangkauan Anda seperti '\ x00' hingga '\ x0020' kode tidak akan menghapusnya.
Muskovets

5

Saya telah mencoba metode berikut dan bahkan bekerja dengan kasus ekstrim seperti:

str1='          I   live    on    earth           '

' '.join(str1.split())

Tetapi jika Anda lebih suka ekspresi reguler, itu bisa dilakukan sebagai:

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

Meskipun beberapa preprocessing harus dilakukan untuk menghilangkan ruang trailing dan ending.


3

Ini juga sepertinya berhasil:

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

Di mana variabel smewakili string Anda.


2

Dalam beberapa kasus itu diinginkan untuk menggantikan kejadian berturut-turut setiap karakter spasi dengan satu contoh dari yang karakter. Anda akan menggunakan ekspresi reguler dengan referensi untuk melakukan itu.

(\s)\1{1,}cocok dengan karakter spasi putih apa pun, diikuti oleh satu atau lebih kemunculan karakter itu. Sekarang, yang perlu Anda lakukan adalah menentukan grup pertama ( \1) sebagai pengganti pertandingan.

Membungkus ini dalam suatu fungsi:

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

Alternatif lain:

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

Satu baris kode untuk menghapus semua spasi tambahan sebelum, sesudah, dan dalam kalimat:

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

Penjelasan:

  1. Pisahkan seluruh string menjadi daftar.
  2. Saring elemen kosong dari daftar.
  3. Bergabung kembali dengan elemen yang tersisa * dengan satu ruang

* Elemen yang tersisa harus kata-kata atau kata-kata dengan tanda baca, dll. Saya tidak menguji ini secara ekstensif, tetapi ini harus menjadi titik awal yang baik. Semua yang terbaik!


2

Solusi untuk pengembang Python:

import re

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

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

Yang tercepat yang Anda dapatkan untuk string yang dibuat pengguna adalah:

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

Hubungan arus pendek membuatnya sedikit lebih cepat daripada jawaban komprehensif pythonlarry . Pergi untuk ini jika Anda mengejar efisiensi dan benar-benar mencari untuk menyingkirkan ruang putih tambahan dari berbagai ruang tunggal .


1

Cukup mengejutkan - tidak ada yang memposting fungsi sederhana yang akan jauh lebih cepat daripada SEMUA solusi diposting lainnya. Ini dia:

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)

Hasil :

Ini adalah string yang penuh dengan spasi dan ketukan


0

Untuk menghilangkan ruang putih, dengan mempertimbangkan spasi di depan, di belakang, dan ruang putih ekstra di antara kata-kata, gunakan:

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

Kesepakatan pertama ordengan ruang putih terdepan, yang kedua orberkaitan dengan dimulainya string ruang putih terkemuka, dan yang terakhir berkaitan dengan ruang putih tertinggal.

Untuk bukti penggunaan, tautan ini akan memberi Anda tes.

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

Ini akan digunakan dengan fungsi re.split .


0

Saya punya metode sederhana yang saya gunakan di perguruan tinggi.

line = "I     have            a       nice    day."

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

Ini akan menggantikan setiap ruang ganda dengan satu ruang dan akan melakukannya 1000 kali. Ini berarti Anda dapat memiliki 2000 ruang ekstra dan masih akan berfungsi. :)


Ini (praktis) identik dengan jawaban Anakimi (diposting lebih dari dua tahun sebelumnya).
Peter Mortensen

0

Saya punya metode sederhana tanpa pemisahan:

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
Apa bedanya dengan jawaban Anakimi (diposting lebih dari tiga tahun sebelumnya)? Bukankah ini versi yang lebih rumit?
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='') 

Hasil:

Hapus semua ruang: Youcanselectbelowtrimsforremovingwhitespace !! BRAliakbar Hapus ruang terdepan: Anda dapat memilih trim di bawah ini untuk menghilangkan ruang putih !! BR Aliakbar
Hapus spasi tambahan: Anda dapat memilih trim di bawah ini untuk menghilangkan spasi putih !! BR Aliakbar Hapus spasi depan dan belakang: Anda dapat memilih trim di bawah ini untuk menghilangkan spasi putih !! BR Aliakbar Hapus lebih dari satu ruang: Anda dapat memilih trim di bawah ini untuk menghilangkan ruang putih !! BR Aliakbar


-1

Saya belum banyak membaca contoh-contoh lain, tetapi saya baru saja membuat metode ini untuk menggabungkan beberapa karakter spasi berturut-turut.

Itu tidak menggunakan perpustakaan apa pun, dan sementara itu relatif panjang dalam hal panjang skrip, itu bukan implementasi yang kompleks:

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)))
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.