Jak usunąć podciąg z końca łańcucha w Pythonie?


382

Mam następujący kod:

url = 'abcdc.com'
print(url.strip('.com'))

Oczekiwałem: abcdc

Mam: abcd

Teraz ja robię

url.rsplit('.com', 1)

Czy jest lepszy sposób?


6
strip usuwa znaki podane z obu końców łańcucha, w twoim przypadku paski „.”, „c”, „o” i „m”.
truppo

6
Spowoduje to również usunięcie tych znaków z przodu łańcucha. Jeśli chcesz tylko usunąć z końca, użyj rstrip ()
Andre Miller

42
Tak. str. pas nie robi tego, co myślisz, że działa. str.strip usuwa dowolny ze znaków określonych na początku i na końcu łańcucha. Zatem „acbacda” .strip („ad”) daje „cbac”; a na początku i da na końcu zostały rozebrane. Twoje zdrowie.
scvalex

2
Dodatkowo usuwa znaki w dowolnej kolejności : „site.ocm”> „site”.
Eric O Lebigot,

1
@scvalex, wow właśnie zdałem sobie sprawę z tego, że używałem go w ten sposób od wieków - jest to niebezpieczne, ponieważ kod często i tak działa
Flash

Odpowiedzi:


556

stripnie znaczy „usuń ten podciąg”. x.strip(y)traktuje yjako zestaw znaków i usuwa wszystkie znaki w tym zestawie z końcówek x.

Zamiast tego możesz użyć endswithi kroić:

url = 'abcdc.com'
if url.endswith('.com'):
    url = url[:-4]

Lub używając wyrażeń regularnych :

import re
url = 'abcdc.com'
url = re.sub('\.com$', '', url)

4
Tak, sam uważam, że pierwszy przykład, z testem endwith (), byłby lepszy; wyrażenie regularne wiązałoby się z pewnym ograniczeniem wydajności (parsowanie wyrażenia regularnego itp.). Nie wybrałbym rsplit (), ale to dlatego, że nie wiem, co dokładnie próbujesz osiągnąć. Sądzę, że usuwa domenę .com, jeśli i tylko wtedy, gdy pojawia się na końcu adresu URL? Rozwiązanie rsplit sprawiłoby ci problemy, gdybyś używał go w nazwach domen takich jak „www.commercialthingie.co.uk”
Steef

13
url = url[:-4] if any(url.endswith(x) for x in ('.com','.net')) else url
Burhan Khalid

1
co jeśli napiszę EXAMLPLE.COMnazwy domen nie rozróżniają wielkości liter. (To jest głosowanie na rozwiązanie wyrażenia regularnego)
Jasen

3
To nie jest przepisywanie, rsplit()rozwiązanie nie zachowuje się tak samo jak to, endswith()gdy oryginalny łańcuch nie ma podłańcucha na końcu, ale gdzieś pośrodku. Na przykład: "www.comeandsee.com".rsplit(".com",1)[0] == "www.comeandsee"ale"www.comeandsee.net".rsplit(".com",1)[0] == "www"
Steef

1
Składnia s[:-n]ma zastrzeżenie: n = 0ponieważ nie zwraca łańcucha z odciętymi ostatnimi znakami zero, lecz pusty ciąg.
BlenderBender

90

Jeśli masz pewność, że ciąg pojawi się tylko na końcu, najprostszym sposobem byłoby użycie polecenia „replace”:

url = 'abcdc.com'
print(url.replace('.com',''))

56
które również zastąpią adres URL podobny www.computerhope.com. sprawdź z endswith()i powinno być w porządku.
ghostdog74

72
"www.computerhope.com".endswith(".com")jest prawdą, wciąż się psuje!

1
„Jeśli masz pewność, że ciąg pojawia się tylko na końcu”, masz na myśli „Jeśli masz pewność, że podciąg pojawi się tylko raz”? wydaje się, że zamień działa również, gdy podłańcuch znajduje się w środku, ale jak sugeruje inny komentarz, zastąpi każde wystąpienie podłańcucha, dlaczego powinno być na końcu, nie rozumiem
idclev 463035818

49
def strip_end(text, suffix):
    if not text.endswith(suffix):
        return text
    return text[:len(text)-len(suffix)]

4
Jeśli wiesz, że sufiks nie jest pusty (na przykład gdy jest stały), to: zwróć tekst [: - len (sufiks)]
MarcH

4
Dzięki. Ostatnią linię można skrócić:return text[:-len(suffix)]
Jabba

3
@Jabba: Niestety, nie będzie to działać w przypadku pustych przyrostków, jak wspomniał fuenfundachtzig.
yairchu

46

Ponieważ wydaje się, że nikt jeszcze tego nie zauważył:

url = "www.example.com"
new_url = url[:url.rfind(".")]

Powinno to być bardziej wydajne niż metody wykorzystujące, split()ponieważ nie jest tworzony nowy obiekt listy, a to rozwiązanie działa dla ciągów z kilkoma kropkami.


Wow, to niezła sztuczka. Nie mogłem tego zawieść, ale miałem również trudności z wymyśleniem sposobów, w jakie może to zawieść. Lubię to, ale jest to bardzo „magiczne”, ciężko jest zrozumieć, co to robi, patrząc na to. Musiałem przetworzyć mentalnie każdą część linii, aby ją „zdobyć”.
DevPlayer,

14
Nie udaje się to, jeśli szukany ciąg NIE jest obecny i zamiast tego błędnie usuwa ostatni znak.
robbat2

25

Zależy od tego, co wiesz o swoim adresie URL i dokładnie, co próbujesz zrobić. Jeśli wiesz, że zawsze kończy się na „.com” (lub „.net” lub „.org”), to

 url=url[:-4]

jest najszybszym rozwiązaniem. Jeśli są to bardziej ogólne adresy URL, prawdopodobnie lepiej jest zajrzeć do biblioteki urlparse dostarczanej z pythonem.

Jeśli natomiast chcesz po prostu usunąć wszystko po finałowym „.” w ciągu

url.rsplit('.',1)[0]

będzie działać. Lub jeśli chcesz po prostu wszystko do pierwszego ”. więc spróbuj

url.split('.',1)[0]

16

Jeśli wiesz, że to rozszerzenie

url = 'abcdc.com'
...
url.rsplit('.', 1)[0]  # split at '.', starting from the right, maximum 1 split

Działa to równie dobrze z abcdc.comlub www.abcdc.comi abcdc.[anything]i jest bardziej rozszerzalne.


12

W jednej linii:

text if not text.endswith(suffix) or len(suffix) == 0 else text[:-len(suffix)]


7

W przypadku adresów URL (ponieważ w podanym przykładzie wydaje się to być częścią tematu) można zrobić coś takiego:

import os
url = 'http://www.stackoverflow.com'
name,ext = os.path.splitext(url)
print (name, ext)

#Or:
ext = '.'+url.split('.')[-1]
name = url[:-len(ext)]
print (name, ext)

Oba będą generować: ('http://www.stackoverflow', '.com')

Można to również połączyć z, str.endswith(suffix)jeśli chcesz po prostu podzielić „.com” lub cokolwiek innego.


5

url.rsplit ('. com', 1)

nie jest całkiem właściwe.

To, co naprawdę musisz napisać, to

url.rsplit('.com', 1)[0]

i wygląda całkiem zwięźle IMHO.

Jednak moją osobistą preferencją jest ta opcja, ponieważ używa tylko jednego parametru:

url.rpartition('.com')[0]

1
Partycja +1 jest preferowana, gdy potrzebny jest tylko jeden podział, ponieważ zawsze zwraca odpowiedź, błąd IndexError nie wystąpi.
Gringo Suave,


2

Jeśli chcesz usunąć jakiś koniec łańcucha, jeśli istnieje, w przeciwnym razie nic nie rób. Moje najlepsze rozwiązania. Prawdopodobnie będziesz chciał skorzystać z jednej z dwóch pierwszych implementacji, jednak dla kompletności podałem trzecią.

Dla stałego przyrostka:

def remove_suffix(v, s):
    return v[:-len(s) if v.endswith(s) else v
remove_suffix("abc.com", ".com") == 'abc'
remove_suffix("abc", ".com") == 'abc'

Dla wyrażenia regularnego:

def remove_suffix_compile(suffix_pattern):
    r = re.compile(f"(.*?)({suffix_pattern})?$")
    return lambda v: r.match(v)[1]
remove_domain = remove_suffix_compile(r"\.[a-zA-Z0-9]{3,}")
remove_domain("abc.com") == "abc"
remove_domain("sub.abc.net") == "sub.abc"
remove_domain("abc.") == "abc."
remove_domain("abc") == "abc"

W przypadku zbioru stałych sufiksów asymptotycznie najszybszy sposób dla dużej liczby połączeń:

def remove_suffix_preprocess(*suffixes):
    suffixes = set(suffixes)
    try:
        suffixes.remove('')
    except KeyError:
        pass

    def helper(suffixes, pos):
        if len(suffixes) == 1:
            suf = suffixes[0]
            l = -len(suf)
            ls = slice(0, l)
            return lambda v: v[ls] if v.endswith(suf) else v
        si = iter(suffixes)
        ml = len(next(si))
        exact = False
        for suf in si:
            l = len(suf)
            if -l == pos:
                exact = True
            else:
                ml = min(len(suf), ml)
        ml = -ml
        suffix_dict = {}
        for suf in suffixes:
            sub = suf[ml:pos]
            if sub in suffix_dict:
                suffix_dict[sub].append(suf)
            else:
                suffix_dict[sub] = [suf]
        if exact:
            del suffix_dict['']
            for key in suffix_dict:
                suffix_dict[key] = helper([s[:pos] for s in suffix_dict[key]], None)
            return lambda v: suffix_dict.get(v[ml:pos], lambda v: v)(v[:pos])
        else:
            for key in suffix_dict:
                suffix_dict[key] = helper(suffix_dict[key], ml)
            return lambda v: suffix_dict.get(v[ml:pos], lambda v: v)(v)
    return helper(tuple(suffixes), None)
domain_remove = remove_suffix_preprocess(".com", ".net", ".edu", ".uk", '.tv', '.co.uk', '.org.uk')

ostatni jest prawdopodobnie znacznie szybszy w pypy niż cpython. Wariant wyrażenia regularnego jest prawdopodobnie szybszy od tego praktycznie we wszystkich przypadkach, w których nie występują duże słowniki potencjalnych przyrostków, których nie można łatwo przedstawić jako wyrażenia regularnego przynajmniej w cPython.

W PyPy wariant wyrażenia regularnego jest prawie na pewno wolniejszy dla dużej liczby wywołań lub długich ciągów, nawet jeśli moduł re używa silnika wyrażeń regularnych kompilujących DFA, ponieważ ogromna większość kosztów lambda zostanie zoptymalizowana przez JIT.

Jednak w cPython fakt, że działający kod c dla wyrażenia regularnego porównuje prawie na pewno możliwości algorytmiczne wersji kolekcji sufiksów w prawie wszystkich przypadkach.


2

Jeśli chcesz tylko usunąć rozszerzenie:

'.'.join('abcdc.com'.split('.')[:-1])
# 'abcdc'

Działa z dowolnym rozszerzeniem, z potencjalnymi innymi kropkami również w nazwie pliku. Po prostu dzieli ciąg jako listę na kropki i łączy go bez ostatniego elementu.


2
import re

def rm_suffix(url = 'abcdc.com', suffix='\.com'):
    return(re.sub(suffix+'$', '', url))

Chcę powtórzyć tę odpowiedź jako najbardziej ekspresyjny sposób na zrobienie tego. Oczywiście, następujące czynności zajmowałyby mniej czasu procesora:

def rm_dotcom(url = 'abcdc.com'):
    return(url[:-4] if url.endswith('.com') else url)

Jeśli jednak procesor jest szyjką butelki, po co pisać w języku Python?

Kiedy w ogóle procesor jest szyjką butelki? Może w sterownikach.

Zaletą używania wyrażeń regularnych jest możliwość ponownego użycia kodu. Co jeśli chcesz następnie usunąć „.me”, który ma tylko trzy znaki?

Ten sam kod zrobiłby lewę:

>>> rm_sub('abcdc.me','.me')
'abcdc'

1

W moim przypadku musiałem zgłosić wyjątek, więc:

class UnableToStripEnd(Exception):
    """A Exception type to indicate that the suffix cannot be removed from the text."""

    @staticmethod
    def get_exception(text, suffix):
        return UnableToStripEnd("Could not find suffix ({0}) on text: {1}."
                                .format(suffix, text))


def strip_end(text, suffix):
    """Removes the end of a string. Otherwise fails."""
    if not text.endswith(suffix):
        raise UnableToStripEnd.get_exception(text, suffix)
    return text[:len(text)-len(suffix)]


1

Zakładając, że chcesz usunąć domenę, bez względu na to, co to jest (.com, .net itp.). Zalecam znalezienie .i usunięcie wszystkiego od tego momentu.

url = 'abcdc.com'
dot_index = url.rfind('.')
url = url[:dot_index]

Tutaj używam rfinddo rozwiązania problemu adresów URL, abcdc.com.netktóre należy sprowadzić do nazwy abcdc.com.

Jeśli martwisz się również o www.s, powinieneś wyraźnie je sprawdzić:

if url.startswith("www."):
   url = url.replace("www.","", 1)

1 w zamianie jest dla dziwnych przypadków edgecases www.net.www.com

Jeśli Twój adres URL stanie się bardziej szalony niż to, spójrz na odpowiedzi wyrażenia regularnego, na które ludzie odpowiedzieli.


1

Użyłem wbudowanej funkcji rstrip, aby wykonać następujące czynności:

string = "test.com"
suffix = ".com"
newstring = string.rstrip(suffix)
print(newstring)
test

Kiepski pomysł. Spróbować "test.ccom".
Shital Shah

Ale nie o to chodzi w tym pytaniu. Poproszono go tylko o usunięcie znanego podłańcucha z końca innego. Działa to dokładnie tak, jak oczekiwano.
Alex

1

Możesz użyć podziału:

'abccomputer.com'.split('.com',1)[0]
# 'abccomputer'

5
Gdy a = 'www.computerbugs.com'wynika to z 'www'
yairchu

0

Jest to idealne zastosowanie do wyrażeń regularnych:

>>> import re
>>> re.match(r"(.*)\.com", "hello.com").group(1)
'hello'

5
Powinieneś także dodać $, aby upewnić się, że pasujesz do nazw hostów kończących się na „.com”.
Cristian Ciupitu

0

Python> = 3,9:

'abcdc.com'.removesuffix('.com')

Python <3.9:

def remove_suffix(text, suffix):
    if text.endswith(suffix):
        text = text[:-len(suffix)]
    return text

remove_suffix('abcdc.com', '.com')

1
Twoja odpowiedź na Python 3.9 jest duplikatem powyższej odpowiedzi . Na twoją odpowiedź dla poprzednich wersji udzielono również wielu odpowiedzi w tym wątku i nie zwróciłoby niczego, gdyby ciąg nie miał sufiksu.
Xavier Guihot
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.