Załóżmy, że ten ciąg:
The fox jumped over the log.
Zmieniając się w:
The fox jumped over the log.
Co jest najprostsze (1-2 wiersze), aby to osiągnąć, bez dzielenia się i przechodzenia na listy?
Załóżmy, że ten ciąg:
The fox jumped over the log.
Zmieniając się w:
The fox jumped over the log.
Co jest najprostsze (1-2 wiersze), aby to osiągnąć, bez dzielenia się i przechodzenia na listy?
Odpowiedzi:
>>> import re
>>> re.sub(' +', ' ', 'The quick brown fox')
'The quick brown fox'
string.split
obsługuje także wszelkiego rodzaju białe znaki.
re.sub(' {2,}', ' ', 'The quick brown fox')
aby zapobiec zbędnemu zastępowaniu pojedynczej spacji pojedynczą spacją .
foo
jest twój ciąg:
" ".join(foo.split())
Ostrzegamy jednak, że usuwa to „wszystkie białe znaki (spacja, tabulator, nowa linia, powrót, formatowanie)” (dzięki hhsaffar , patrz komentarze). Tj. "this is \t a test\n"
Skutecznie skończy jako "this is a test"
.
import re
s = "The fox jumped over the log."
re.sub("\s\s+" , " ", s)
lub
re.sub("\s\s+", " ", s)
ponieważ przestrzeń przed przecinkiem jest wymieniona jako wkurzony w PEP 8 , o czym wspomniał użytkownik Martin Thoma w komentarzach.
r"\s\s+"
aby nie próbowało zastępować już pojedynczych spacji.
"\s{2,}"
zamiast obejścia problemu polegającego na tym, że nie znasz średnio zaawansowanych wyrażeń regularnych?
s
, ale zwraca nową wartość.
\s+
spowodowałoby, że wiersz brzmiałby „zamień jedną lub więcej spacji spacją”, zamiast „zamień dwie lub więcej spacji spacją”. Ten pierwszy natychmiast każe mi się zatrzymać i pomyśleć „Po co zastępować jedną spację jedną spacją? To głupie”. Dla mnie jest to (bardzo niewielki) zapach kodu. I faktycznie nie można oczekiwać, aby istniała jakakolwiek różnica wydajności w ogóle między nimi, jak to będzie skopiowanie do nowego łańcucha tak, i musi się zatrzymać i test niezależnie od tego, gdzie przestrzeń jest kopiowany z .
\s\s+
ponieważ nie spowoduje to normalizacji znaku TAB na normalnym polu. SPACE + TAB zostaje zastąpiony w ten sposób.
Używanie wyrażeń regularnych z „\ s” i wykonywanie prostych string.split () spowoduje również usunięcie innych białych znaków - takich jak znaki nowej linii, znaki powrotu karetki, tabulatory. Jeśli nie jest to pożądane, aby zrobić tylko wiele spacji , przedstawiam te przykłady.
Użyłem 11 akapitów, 1000 słów, 6665 bajtów Lorem Ipsum, aby uzyskać realistyczne testy czasu i użyłem dodatkowych spacji o losowej długości w całym:
original_string = ''.join(word + (' ' * random.randint(1, 10)) for word in lorem_ipsum.split(' '))
One-liner zasadniczo wykona pasek dowolnych spacji wiodących / spływowych i zachowa spację wiodącą / spływową (ale tylko JEDEN ;-).
# 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
UWAGA: „ Należy pamiętać, że głównym while
Wersja” wykonała kopię original_string
, jak sądzę, że raz zmodyfikowana przy pierwszym uruchomieniu, kolejne uruchomienia byłyby szybsze (choćby odrobinę). Ponieważ to dodaje czasu, dodałem tę kopię ciągu do pozostałych dwóch, aby czasy pokazały różnicę tylko w logice. stmt
na timeit
instancji zostanie wykonany tylko raz ; oryginalny sposób, w jaki to zrobiłem, while
pętla działała na tej samej etykiecie original_string
, więc przy drugim uruchomieniu nie byłoby nic do zrobienia. Sposób, w jaki jest teraz skonfigurowany, wywoływanie funkcji, używanie dwóch różnych etykiet, nie stanowi problemu. Dodałem assert
oświadczenia do wszystkich pracowników, aby sprawdzić, czy zmieniamy coś w każdej iteracji (dla tych, którzy mogą być wątpliwi). Np. Zmień to i psuje się:
# 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
W przypadku trywialnego ciągu wydaje się, że pętla while jest najszybsza, po niej następuje Pythonic split-split / join i regex ciągnący w górę.
W przypadku nietrywialnych ciągów wydaje się, że jest jeszcze coś do rozważenia. 32-bitowy 2.7? To wyrażenie regularne na ratunek! 2.7 64-bitowy? while
Pętla jest najlepszy, o przyzwoitej marży. 32-bit 3.2, wybierz „właściwe” join
. 64-bit 3.3, idź na while
pętlę. Jeszcze raz.
Ostatecznie można poprawić wydajność, jeśli / gdzie / kiedy jest to potrzebne , ale zawsze najlepiej jest pamiętać mantrę :
IANAL, YMMV, Caveat Emptor!
' '.join(the_string.split())
ponieważ jest to zwykły przypadek użycia, ale chciałbym podziękować za twoją pracę!
' '.join(p for p in s.split(' ') if p)
<- nadal stracił ołów / końcowe spacje, ale uwzględniał wiele spacji. Aby je zatrzymać, musisz zrobić jak parts = s.split(' '); (' ' if not parts[0] else '') + ' '.join(p for p in s.split(' ') if p) + (' ' if not parts[-1] else '')
!
Muszę się zgodzić z komentarzem Paula McGuire'a. Dla mnie,
' '.join(the_string.split())
jest zdecydowanie lepsza niż wyrzucenie wyrażenia regularnego.
Moje pomiary (Linux i Python 2.5) pokazują, że łączenie z podziałem, a następnie łączenie jest prawie pięciokrotnie szybsze niż wykonanie polecenia „re.sub (...)” i wciąż trzykrotnie szybsze, jeśli raz skompilujesz wyrażenie regularne i wykonasz operację wiele razy. I pod każdym względem jest łatwiejszy do zrozumienia - znacznie bardziej Pythonic.
import re
string = re.sub('[ \t\n]+', ' ', 'The quick brown \n\n \t fox')
Spowoduje to usunięcie wszystkich zakładek, nowych linii i wielu białych spacji z pojedynczą białą spacją.
Wypróbowałem następującą metodę, która działa nawet w ekstremalnych przypadkach, takich jak:
str1=' I live on earth '
' '.join(str1.split())
Ale jeśli wolisz wyrażenie regularne, możesz to zrobić jako:
re.sub('\s+', ' ', str1)
Chociaż konieczne jest pewne wstępne przetwarzanie w celu usunięcia spacji końcowej i końcowej.
W niektórych przypadkach pożądane jest zastąpienie kolejnych wystąpień każdego znaku spacji jednym wystąpieniem tego znaku. W tym celu użyłbyś wyrażenia regularnego z odnośnikami zwrotnymi.
(\s)\1{1,}
dopasowuje dowolny znak spacji, po którym następuje jedno lub więcej wystąpień tego znaku. Teraz wszystko, co musisz zrobić, to określić pierwszą grupę ( \1
) jako zamiennik dopasowania.
Zawijanie tego w funkcję:
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'
Jeden wiersz kodu, aby usunąć wszystkie dodatkowe spacje przed, po i wewnątrz zdania:
sentence = " The fox jumped over the log. "
sentence = ' '.join(filter(None,sentence.split(' ')))
Wyjaśnienie:
* Pozostałe elementy powinny być słowami lub słowami ze znakami interpunkcyjnymi itp. Nie testowałem tego obszernie, ale powinien to być dobry punkt wyjścia. Wszystkiego najlepszego!
Rozwiązanie dla programistów Python:
import re
text1 = 'Python Exercises Are Challenging Exercises'
print("Original string: ", text1)
print("Without extra spaces: ", re.sub(' +', ' ', text1))
Wynik:
Original string: Python Exercises Are Challenging Exercises
Without extra spaces: Python Exercises Are Challenging Exercises
Najszybsze, jakie możesz uzyskać dla ciągów generowanych przez użytkownika, to:
if ' ' in text:
while ' ' in text:
text = text.replace(' ', ' ')
Krótkie spięcie sprawia, że jest to nieco szybsze niż wyczerpująca odpowiedź pythonlarry . Skorzystaj z tego, jeśli zależy Ci na wydajności i chcesz oddzielić dodatkowe białe spacje odmiany pojedynczej przestrzeni .
Całkiem zaskakujące - nikt nie opublikował prostej funkcji, która będzie znacznie szybsza niż WSZYSTKIE inne publikowane rozwiązania. Oto jest:
def compactSpaces(s):
os = ""
for c in s:
if c != " " or os[-1] != " ":
os += c
return os
Jeśli masz do czynienia z białymi znakami, podział na None nie uwzględni pustego ciągu w zwracanej wartości.
string = 'This is a string full of spaces and taps'
string = string.split(' ')
while '' in string:
string.remove('')
string = ' '.join(string)
print(string)
Wyniki :
To ciąg pełen spacji i kranów
Aby usunąć spację, uwzględniając początkowe, końcowe i dodatkowe białe spacje między wyrazami, użyj:
(?<=\s) +|^ +(?=\s)| (?= +[\n\0])
Pierwszy or
dotyczy wiodącej białej przestrzeni, drugior
dotyczy początku sznurka wiodącej białej przestrzeni, a ostatni dotyczy końcowej białej spacji.
W celu potwierdzenia użycia ten link zapewni Ci test.
https://regex101.com/r/meBYli/4
Należy tego używać z funkcją re.split .
Mam swoją prostą metodę, której użyłem na studiach.
line = "I have a nice day."
end = 1000
while end != 0:
line.replace(" ", " ")
end -= 1
To zastąpi każde podwójne miejsce pojedynczym miejscem i zrobi to 1000 razy. Oznacza to, że możesz mieć 2000 dodatkowych miejsc i nadal będzie działać. :)
Mam prostą metodę bez podziału:
a = "Lorem Ipsum Darum Diesrum!"
while True:
count = a.find(" ")
if count > 0:
a = a.replace(" ", " ")
count = a.find(" ")
continue
else:
break
print(a)
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='')
Wynik:
Usuń wszystkie spacje: Możesz wybrać następujące wykończenia do usunięcia białej przestrzeni! BRAliakbar Usuń spację wiodącą: Możesz wybrać poniżej wykończenia do usunięcia białych spacji !! BR Aliakbar
Usuń końcowe spacje: Możesz wybrać poniższe wykończenia w celu usunięcia białych spacji !! BR Aliakbar Usuń wiodące i końcowe spacje: Możesz wybrać poniżej wykończenia w celu usunięcia białych spacji !! BR Aliakbar Usuń więcej niż jedną spację: Możesz wybrać poniższe wykończenia w celu usunięcia spacji !! BR Aliakbar
Nie czytałem dużo w innych przykładach, ale właśnie stworzyłem tę metodę do konsolidacji wielu kolejnych znaków spacji.
Nie korzysta z żadnych bibliotek i chociaż jest stosunkowo długi pod względem długości skryptu, nie jest złożoną implementacją:
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)))