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?

TIMEX
sumber
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'
Josh Lee
sumber
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".

Taylor Leese
sumber
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.

Nasir
sumber
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!

pythonlarry
sumber
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.

Kevin Little
sumber
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.'
Peter
sumber
11

Sebuah soultion sederhana

>>> import re
>>> s="The   fox jumped   over    the log."
>>> print re.sub('\s+',' ', s)
The fox jumped over the log.
HMS
sumber
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(' ')
devinbost
sumber
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.

Rakesh Kumar
sumber
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.

ravi tanwar
sumber
3

Ini juga sepertinya berhasil:

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

Di mana variabel smewakili string Anda.

Anakimi
sumber
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'
kubah
sumber
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
Kreshnik
sumber
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!

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

Chadee Fouad
sumber
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())
jw51
sumber
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 .

Hassan Baig
sumber
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
rafal chlopek
sumber
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

Hassan Abdul-Kareem
sumber
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 .

CameronE
sumber
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. :)

Peter Mortensen
sumber
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)
Balduin Scheffbuch
sumber
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

Aliakbar Hosseinzadeh
sumber
-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)))
Scott Anderson
sumber