Hat Python eine String-enthält-Teilstring-Methode?

3599

Ich suche eine string.containsoder string.indexofMethode in Python.

Ich will das tun:

if not somestring.contains("blah"):
   continue
Blankman
quelle

Antworten:

6261

Sie können den inOperator verwenden :

if "blah" not in somestring: 
    continue
Michael Mrozek
quelle
231
Unter der Haube wird Python verwenden __contains__(self, item), __iter__(self)und __getitem__(self, key)in dieser Reihenfolge , ob ein Element liegt enthält in einem bestimmten zu bestimmen. Implementieren Sie mindestens eine dieser Methoden, um insie Ihrem benutzerdefinierten Typ zur Verfügung zu stellen.
BallpointBen
27
Stellen Sie nur sicher, dass Somestring nicht None ist. Andernfalls erhalten Sie einenTypeError: argument of type 'NoneType' is not iterable
Big Pumpkin
5
FWIW, dies ist der idiomatische Weg, um dieses Ziel zu erreichen.
Trenton
6
inVerwendet der Python- Operator für Zeichenfolgen den Rabin-Carp-Algorithmus?
Sam Chats
3
@SamChats siehe stackoverflow.com/questions/18139660/… für die Implementierungsdetails (in CPython; afaik schreibt die Sprachspezifikation hier keinen bestimmten Algorithmus vor).
Christoph Burschka
667

Wenn es sich nur um eine Teilstringsuche handelt, können Sie diese verwenden string.find("substring").

Sie haben mit ein wenig vorsichtig sein find, indexund inwenn es , wie sie Suchen werden String. Mit anderen Worten:

s = "This be a string"
if s.find("is") == -1:
    print("No 'is' here!")
else:
    print("Found 'is' in the string.")

Es würde Found 'is' in the string.ähnlich drucken , if "is" in s:würde auswerten True. Dies kann sein oder nicht, was Sie wollen.

Eldarerathis
quelle
78
+1 zum Hervorheben der Fallstricke bei der Suche nach Teilzeichenfolgen. Die offensichtliche Lösung ist, if ' is ' in s:welche Falsewie (wahrscheinlich) erwartet zurückkehren wird.
Aaronasterling
95
@aaronasterling Offensichtlich mag es sein, aber nicht ganz richtig. Was ist, wenn Sie Interpunktion haben oder am Anfang oder Ende? Was ist mit Kapitalisierung? Besser wäre eine Suche ohne Berücksichtigung von Groß- und Kleinschreibung \bis\b(Wortgrenzen).
Bob
2
@JamieBull Noch einmal, Sie müssen überlegen, ob Sie Interpunktion als Trennzeichen für ein Wort einfügen möchten. Das Aufteilen hätte weitgehend den gleichen Effekt wie die naive Lösung der Überprüfung ' is ', insbesondere, ob es nicht fängt This is, a comma'oder nicht 'It is.'.
Bob
7
@JamieBull: Ich bezweifle sehr, dass sich eine echte Eingabeaufteilung s.split(string.punctuation + string.whitespace)auch nur einmal aufteilen würde. splitist nicht wie die strip/ rstrip/ lstripFunktionsfamilie, sondern wird nur aufgeteilt, wenn alle Trennzeichen zusammenhängend in genau dieser Reihenfolge angezeigt werden. Wenn Sie nach Zeichenklassen aufteilen möchten, kehren Sie zu regulären Ausdrücken zurück (an diesem Punkt ist das Suchen r'\bis\b'ohne Aufteilen der einfachere und schnellere Weg).
ShadowRanger
8
'is' not in (w.lower() for w in s.translate(string.maketrans(' ' * len(string.punctuation + string.whitespace), string.punctuation + string.whitespace)).split()- Ok, Punkt genommen. Das ist jetzt lächerlich ...
Jamie Bull
190

Hat Python eine Zeichenfolge, die eine Teilzeichenfolgenmethode enthält?

Ja, aber Python verfügt über einen Vergleichsoperator, den Sie stattdessen verwenden sollten, da die Sprache ihre Verwendung beabsichtigt und andere Programmierer von Ihnen erwarten, dass Sie ihn verwenden. Das Schlüsselwort lautet in, das als Vergleichsoperator verwendet wird:

>>> 'foo' in '**foo**'
True

Das Gegenteil (Ergänzung), nach dem die ursprüngliche Frage fragt, ist not in:

>>> 'foo' not in '**foo**' # returns False
False

Dies ist semantisch dasselbe wie, not 'foo' in '**foo**'aber es ist viel besser lesbar und in der Sprache explizit als Verbesserung der Lesbarkeit vorgesehen.

Vermeiden Sie __contains__, findundindex

Wie versprochen, hier ist die containsMethode:

str.__contains__('**foo**', 'foo')

kehrt zurück True. Sie können diese Funktion auch von der Instanz des Superstrings aus aufrufen:

'**foo**'.__contains__('foo')

Aber nicht. Methoden, die mit Unterstrichen beginnen, werden als semantisch privat betrachtet. Der einzige Grund, dies zu verwenden, ist die Erweiterung der inund not in-Funktionalität (z. B. bei Unterklassen str):

class NoisyString(str):
    def __contains__(self, other):
        print('testing if "{0}" in "{1}"'.format(other, self))
        return super(NoisyString, self).__contains__(other)

ns = NoisyString('a string with a substring inside')

und nun:

>>> 'substring' in ns
testing if "substring" in "a string with a substring inside"
True

Vermeiden Sie außerdem die folgenden Zeichenfolgenmethoden:

>>> '**foo**'.index('foo')
2
>>> '**foo**'.find('foo')
2

>>> '**oo**'.find('foo')
-1
>>> '**oo**'.index('foo')

Traceback (most recent call last):
  File "<pyshell#40>", line 1, in <module>
    '**oo**'.index('foo')
ValueError: substring not found

In anderen Sprachen gibt es möglicherweise keine Methoden zum direkten Testen auf Teilzeichenfolgen. Daher müssten Sie diese Arten von Methoden verwenden. Mit Python ist es jedoch wesentlich effizienter, den inVergleichsoperator zu verwenden .

Leistungsvergleiche

Wir können verschiedene Wege vergleichen, um dasselbe Ziel zu erreichen.

import timeit

def in_(s, other):
    return other in s

def contains(s, other):
    return s.__contains__(other)

def find(s, other):
    return s.find(other) != -1

def index(s, other):
    try:
        s.index(other)
    except ValueError:
        return False
    else:
        return True



perf_dict = {
'in:True': min(timeit.repeat(lambda: in_('superstring', 'str'))),
'in:False': min(timeit.repeat(lambda: in_('superstring', 'not'))),
'__contains__:True': min(timeit.repeat(lambda: contains('superstring', 'str'))),
'__contains__:False': min(timeit.repeat(lambda: contains('superstring', 'not'))),
'find:True': min(timeit.repeat(lambda: find('superstring', 'str'))),
'find:False': min(timeit.repeat(lambda: find('superstring', 'not'))),
'index:True': min(timeit.repeat(lambda: index('superstring', 'str'))),
'index:False': min(timeit.repeat(lambda: index('superstring', 'not'))),
}

Und jetzt sehen wir, dass die Verwendung inviel schneller ist als die anderen. Weniger Zeit für eine gleichwertige Operation ist besser:

>>> perf_dict
{'in:True': 0.16450627865128808,
 'in:False': 0.1609668098178645,
 '__contains__:True': 0.24355481654697542,
 '__contains__:False': 0.24382793854783813,
 'find:True': 0.3067379407923454,
 'find:False': 0.29860888058124146,
 'index:True': 0.29647137792585454,
 'index:False': 0.5502287584545229}
Aaron Hall
quelle
6
Warum sollte man vermeiden str.indexund str.find? Wie sonst würden Sie vorschlagen, dass jemand den Index eines Teilstrings findet, anstatt nur, ob er existiert oder nicht? (oder s.find(ss) != -1ss in s
meintest
3
Genau so, obwohl die Absicht hinter der Verwendung dieser Methoden durch die elegante Verwendung des reModuls besser angegangen werden kann . Ich habe noch keine Verwendung für str.index gefunden oder finde mich in einem Code, den ich noch geschrieben habe.
Aaron Hall
Bitte erweitern Sie Ihre Antwort auf Ratschläge gegen die Verwendung str.countvon ( string.count(something) != 0). Schauder
CS95
Wie funktioniert die operatorModulversion ?
jpmc26
@ jpmc26 es ist das gleiche wie in_oben - aber mit einem Stackframe um es herum, also ist es langsamer als das: github.com/python/cpython/blob/3.7/Lib/operator.py#L153
Aaron Hall
175

if needle in haystack:ist die normale Verwendung, wie @Michael sagt - sie basiert auf dem inOperator, ist lesbarer und schneller als ein Methodenaufruf.

Wenn Sie wirklich eine Methode anstelle eines Operators benötigen (z. B. um etwas Seltsames key=für eine sehr eigenartige Art zu tun ...?), Wäre dies der Fall 'haystack'.__contains__. Aber da dein Beispiel für die Verwendung in einem ist if, meinst du wohl nicht wirklich, was du sagst ;-). Es ist keine gute Form (weder lesbar noch effizient), spezielle Methoden direkt zu verwenden - sie sollen stattdessen über die Operatoren und integrierten Funktionen verwendet werden, die an sie delegieren.

Alex Martelli
quelle
55

in Python-Zeichenfolgen und -Listen

Hier einige nützliche Beispiele, die für die inMethode sprechen :

"foo" in "foobar"
True

"foo" in "Foobar"
False

"foo" in "Foobar".lower()
True

"foo".capitalize() in "Foobar"
True

"foo" in ["bar", "foo", "foobar"]
True

"foo" in ["fo", "o", "foobar"]
False

["foo" in a for a in ["fo", "o", "foobar"]]
[False, False, True]

Vorbehalt. Listen sind iterable und die inMethode wirkt auf iterables, nicht nur auf Strings.

Firelynx
quelle
1
Könnte die iterierbare Liste umgeschaltet werden, um nach einer der Listen in einer einzelnen Zeichenfolge zu suchen? Bsp. : ["bar", "foo", "foobar"] in "foof"?
CaffeinatedCoder
1
@CaffeinatedCoder, nein, dies erfordert eine verschachtelte Iteration. „|“ .Join ([ „bar“, „foo“, „foobar“]) und die Erstellung einer Regex aus ihm heraus, dann passend auf „foof“ am besten durch Verbinden der Liste mit Rohren getan
firelynx
2
any ([x in "foof" für x in ["bar", "foo", "foobar"]])
Izaak Weiss
1
@IzaakWeiss Ihr One Liner funktioniert, ist aber nicht sehr lesbar und führt eine verschachtelte Iteration durch. Ich würde davon
abraten
1
@ PiyushS.Wanare was meinst du mit Komplexität? Die "WTF / min" ist bei Regex viel höher.
Firelynx
42

Wenn Sie zufrieden sind, "blah" in somestringaber möchten, dass es sich um einen Funktions- / Methodenaufruf handelt, können Sie dies wahrscheinlich tun

import operator

if not operator.contains(somestring, "blah"):
    continue

Alle Operatoren in Python befinden sich mehr oder weniger im Operatormodul einschließlich in.

Jeffrey04
quelle
40

Anscheinend gibt es nichts Vergleichbares für den vektorweisen Vergleich. Ein naheliegender Python-Weg wäre:

names = ['bob', 'john', 'mike']
any(st in 'bob and john' for st in names) 
>> True

any(st in 'mary and jane' for st in names) 
>> False
Ufos
quelle
1
Das liegt daran, dass es unzählige Möglichkeiten gibt, ein Produkt aus atomaren Variablen zu erstellen. Sie können sie in ein Tupel, eine Liste (die Formen kartesischer Produkte sind und eine implizite Reihenfolge enthalten) einfügen, oder sie können als Eigenschaften einer Klasse (keine Reihenfolge von vornherein) oder als Wörterbuchwerte oder als Dateien in Dateien bezeichnet werden ein Verzeichnis oder was auch immer. Wann immer Sie etwas in einem 'Container' oder 'Kontext' eindeutig identifizieren (iterieren oder abrufen) können, können Sie diesen 'Container' als eine Art Vektor sehen und binäre Operationen darauf definieren. en.wikipedia.org/wiki/…
Niriel
Es ist nichts wert, was innicht mit Listen verwendet werden sollte, da es einen linearen Scan der Elemente durchführt und im Vergleich langsam ist. Verwenden Sie stattdessen einen Satz, insbesondere wenn Mitgliedschaftstests wiederholt durchgeführt werden sollen.
CS95
22

Sie können verwenden y.count().

Es wird der ganzzahlige Wert zurückgegeben, wie oft eine Unterzeichenfolge in einer Zeichenfolge angezeigt wird.

Zum Beispiel:

string.count("bah") >> 0
string.count("Hello") >> 1
Brandon Bailey
quelle
8
Das Zählen einer Zeichenfolge ist kostspielig, wenn Sie nur überprüfen möchten, ob sie vorhanden ist ...
Jean-François Fabre
3
Methoden, die im ursprünglichen Beitrag von 2010 vorhanden waren, so dass ich sie mit Konsens der Community herausgearbeitet habe (siehe Meta-Beitrag meta.stackoverflow.com/questions/385063/… )
Jean-François Fabre
17
Nein. Mein Punkt ist "warum genau das gleiche beantworten wie andere vor 9 Jahren"?
Jean-François Fabre
10
weil ich die Seite moderiere ... Ich habe die Frage auf meta meta.stackoverflow.com/questions/385063/… gestellt
Jean-François Fabre
2
Wenn Sie die Berechtigung haben, es zu entfernen, entfernen Sie es. Andernfalls tun Sie, was Sie müssen, und fahren Sie fort. IMO diese Antwort Mehrwert, der sich in Up-Votes von Benutzern widerspiegelt.
Brandon Bailey
20

Hier ist deine Antwort:

if "insert_char_or_string_here" in "insert_string_to_search_here":
    #DOSTUFF

Um zu überprüfen, ob es falsch ist:

if not "insert_char_or_string_here" in "insert_string_to_search_here":
    #DOSTUFF

ODER:

if "insert_char_or_string_here" not in "insert_string_to_search_here":
    #DOSTUFF
ytpillai
quelle
8

Sie können reguläre Ausdrücke verwenden, um die Vorkommen abzurufen:

>>> import re
>>> print(re.findall(r'( |t)', to_search_in)) # searches for t or space
['t', ' ', 't', ' ', ' ']
Muskovets
quelle