Wie erhalte ich einen String nach einem bestimmten Teilstring?

226

Wie kann ich einen String nach einem bestimmten Teilstring erhalten?

Zum Beispiel möchte ich die Zeichenfolge nach "world"in erhaltenmy_string="hello python world , i'm a beginner "

Havox
quelle

Antworten:

399

Der einfachste Weg ist wahrscheinlich, nur Ihr Zielwort aufzuteilen

my_string="hello python world , i'm a beginner "
print my_string.split("world",1)[1] 

Beim Teilen wird das Wort (oder Zeichen) geteilt und optional die Anzahl der Teilungen begrenzt.

In diesem Beispiel auf "Welt" aufteilen und auf nur eine Aufteilung beschränken.

Joran Beasley
quelle
Wenn ich einen Text mit dem 'niedrigen' Wort teilen muss und es das niedrigere Wort davor enthält, funktioniert dies nicht!
Leonardo Hermoso
1
Sie würden einfach 2x teilentarget.split('lower',1)[-1].split('low',1)[-1]
Joran Beasley
Was wäre, wenn der Satz "Hallo Python Megaworld Welt, ich bin ein Anfänger" wäre. Wie kann ich dafür sorgen, dass das ganze Wort und nicht Teil eines anderen als "Megaworld" betrachtet wird? Vielen Dank
pbou
1
dann ist die Zeichenfolge, die Sie suchen, "Welt" ... oder verwenden Sie Regex für Wortgrenzen
Joran Beasley
6
my_string.partition("world")[-1](oder ...[2]) ist schneller.
Martijn Pieters
66
s1 = "hello python world , i'm a beginner "
s2 = "world"

print s1[s1.index(s2) + len(s2):]

Wenn Sie sich mit dem Fall befassen möchten, in dem s2es nicht vorhanden ist s1, verwenden Sie s1.find(s2)im Gegensatz zu index. Wenn der Rückgabewert dieses Aufrufs ist -1, s2ist er nicht in s1.

arshajii
quelle
Sie erhalten eindeutige IDs (die durch mehrere Tausend getrennt sind) ... Ich bin nicht sicher, ob Sie damit keine unnötigen Teilzeichenfolgen erstellen
Joran Beasley
@JoranBeasley, wir rufen nur index (), len () und Slice auf. Es gibt keinen Grund für index () und len (), Teilzeichenfolgen zu erstellen, und wenn dies der Fall ist (ich kann es kaum glauben), ist dies nur ein unnötiges Implementierungsdetail. Gleiches gilt für Slice - es gibt keinen Grund dafür, andere als die zurückgegebenen Teilzeichenfolgen zu erstellen.
Shx2
@ shx2print( s1[s1.index(s2) + len(s2):] is s1[s1.index(s2) + len(s2):])
Joran Beasley
@JoranBeasley Welchen Punkt versuchst du mit diesem Snippet zu machen? Dass bei mehreren Anrufen unterschiedliche Objekte zurückgegeben werden? Mit "unnötigen Teilzeichenfolgen" meine ich andere als die zurückgegebenen Teilzeichenfolgen, dh Teilzeichenfolgen, die nicht erstellt werden müssen, um das Ergebnis abzuleiten.
Shx2
56

Ich bin überrascht, dass niemand etwas erwähnt hat partition.

def substring_after(s, delim):
    return s.partition(delim)[2]

IMHO ist diese Lösung besser lesbar als die von @ arshajii. Abgesehen davon denke ich, dass @ arshajiis das Beste ist, um am schnellsten zu sein - es werden keine unnötigen Kopien / Teilzeichenfolgen erstellt.

shx2
quelle
2
Dies ist eine gute Lösung und behandelt den Fall, dass der Teilstring nicht gut Teil des Basisstrings ist.
Mattmc3
Sie erhalten eindeutige IDs (die durch mehrere Tausend getrennt sind) ... Ich bin nicht sicher, ob Sie damit keine unnötigen Teilzeichenfolgen erstellen (und ich bin zu faul, um sie richtig zu profilieren)
Joran Beasley
1
@JoranBeasley es deutlich macht unnötige substings erstellen. Ich denke, Sie haben meine Antwort falsch verstanden.
shx2
(Arashi auch, denke ich ...)
Joran Beasley
3
Darüber hinaus ist dies schneller als str.split(..., 1).
Martijn Pieters
20

Sie möchten verwenden str.partition() :

>>> my_string.partition("world")[2]
" , i'm a beginner "

weil diese Option schneller ist als die Alternativen .

Beachten Sie, dass dies eine leere Zeichenfolge erzeugt, wenn das Trennzeichen fehlt:

>>> my_string.partition("Monty")[2]  # delimiter missing
''

Wenn Sie die ursprüngliche Zeichenfolge haben möchten, testen Sie, ob der zweite Wert von zurückgegeben wurdestr.partition() nicht leer ist:

prefix, success, result = my_string.partition(delimiter)
if not success: result = prefix

Sie könnten auch verwenden str.split() mit einem Limit von 1 verwenden:

>>> my_string.split("world", 1)[-1]
" , i'm a beginner "
>>> my_string.split("Monty", 1)[-1]  # delimiter missing
"hello python world , i'm a beginner "

Diese Option ist jedoch langsamer . Für ein Best-Case-Szenario str.partition()ist es leicht etwa 15% schneller alsstr.split() :

                                missing        first         lower         upper          last
      str.partition(...)[2]:  [3.745 usec]  [0.434 usec]  [1.533 usec]  <3.543 usec>  [4.075 usec]
str.partition(...) and test:   3.793 usec    0.445 usec    1.597 usec    3.208 usec    4.170 usec
      str.split(..., 1)[-1]:  <3.817 usec>  <0.518 usec>  <1.632 usec>  [3.191 usec]  <4.173 usec>
            % best vs worst:         1.9%         16.2%          6.1%          9.9%          2.3%

Dies zeigt die Zeiten pro Ausführung mit Eingaben, bei denen das Trennzeichen entweder fehlt (Worst-Case-Szenario), zuerst platziert wird (Best-Case-Szenario) oder in der unteren Hälfte, oberen Hälfte oder letzten Position. Die schnellste Zeit ist mit [...]und <...>die schlechteste markiert.

Die obige Tabelle wird durch ein umfassendes Zeitfahren für alle drei unten erstellten Optionen erstellt. Ich habe die Tests auf Python 3.7.4 auf einem 15 "Macbook Pro 2017 mit 2,9 GHz Intel Core i7 und 16 GB RAM durchgeführt.

Dieses Skript generiert zufällige Sätze mit und ohne das zufällig ausgewählte Trennzeichen und führt, falls vorhanden, an verschiedenen Positionen im generierten Satz die Tests in zufälliger Reihenfolge mit Wiederholungen aus (wobei die fairsten Ergebnisse erzielt werden, die zufällige OS-Ereignisse berücksichtigen, die während des Tests stattfinden). und druckt dann eine Tabelle mit den Ergebnissen:

import random
from itertools import product
from operator import itemgetter
from pathlib import Path
from timeit import Timer

setup = "from __main__ import sentence as s, delimiter as d"
tests = {
    "str.partition(...)[2]": "r = s.partition(d)[2]",
    "str.partition(...) and test": (
        "prefix, success, result = s.partition(d)\n"
        "if not success: result = prefix"
    ),
    "str.split(..., 1)[-1]": "r = s.split(d, 1)[-1]",
}

placement = "missing first lower upper last".split()
delimiter_count = 3

wordfile = Path("/usr/dict/words")  # Linux
if not wordfile.exists():
    # macos
    wordfile = Path("/usr/share/dict/words")
words = [w.strip() for w in wordfile.open()]

def gen_sentence(delimiter, where="missing", l=1000):
    """Generate a random sentence of length l

    The delimiter is incorporated according to the value of where:

    "missing": no delimiter
    "first":   delimiter is the first word
    "lower":   delimiter is present in the first half
    "upper":   delimiter is present in the second half
    "last":    delimiter is the last word

    """
    possible = [w for w in words if delimiter not in w]
    sentence = random.choices(possible, k=l)
    half = l // 2
    if where == "first":
        # best case, at the start
        sentence[0] = delimiter
    elif where == "lower":
        # lower half
        sentence[random.randrange(1, half)] = delimiter
    elif where == "upper":
        sentence[random.randrange(half, l)] = delimiter
    elif where == "last":
        sentence[-1] = delimiter
    # else: worst case, no delimiter

    return " ".join(sentence)

delimiters = random.choices(words, k=delimiter_count)
timings = {}
sentences = [
    # where, delimiter, sentence
    (w, d, gen_sentence(d, w)) for d, w in product(delimiters, placement)
]
test_mix = [
    # label, test, where, delimiter sentence
    (*t, *s) for t, s in product(tests.items(), sentences)
]
random.shuffle(test_mix)

for i, (label, test, where, delimiter, sentence) in enumerate(test_mix, 1):
    print(f"\rRunning timed tests, {i:2d}/{len(test_mix)}", end="")
    t = Timer(test, setup)
    number, _ = t.autorange()
    results = t.repeat(5, number)
    # best time for this specific random sentence and placement
    timings.setdefault(
        label, {}
    ).setdefault(
        where, []
    ).append(min(dt / number for dt in results))

print()

scales = [(1.0, 'sec'), (0.001, 'msec'), (1e-06, 'usec'), (1e-09, 'nsec')]
width = max(map(len, timings))
rows = []
bestrow = dict.fromkeys(placement, (float("inf"), None))
worstrow = dict.fromkeys(placement, (float("-inf"), None))

for row, label in enumerate(tests):
    columns = []
    worst = float("-inf")
    for p in placement:
        timing = min(timings[label][p])
        if timing < bestrow[p][0]:
            bestrow[p] = (timing, row)
        if timing > worstrow[p][0]:
            worstrow[p] = (timing, row)
        worst = max(timing, worst)
        columns.append(timing)

    scale, unit = next((s, u) for s, u in scales if worst >= s)
    rows.append(
        [f"{label:>{width}}:", *(f" {c / scale:.3f} {unit} " for c in columns)]
    )

colwidth = max(len(c) for r in rows for c in r[1:])
print(' ' * (width + 1), *(p.center(colwidth) for p in placement), sep="  ")
for r, row in enumerate(rows):
    for c, p in enumerate(placement, 1):
        if bestrow[p][1] == r:
            row[c] = f"[{row[c][1:-1]}]"
        elif worstrow[p][1] == r:
            row[c] = f"<{row[c][1:-1]}>"
    print(*row, sep="  ")

percentages = []
for p in placement:
    best, worst = bestrow[p][0], worstrow[p][0]
    ratio = ((worst - best) / worst)
    percentages.append(f"{ratio:{colwidth - 1}.1%} ")

print("% best vs worst:".rjust(width + 1), *percentages, sep="  ")
Martijn Pieters
quelle
gute Antwort! vor allem, weil Sie den wahren Grund
angeben,
18

Wenn Sie dies mit Regex tun möchten, können Sie einfach eine nicht erfassende Gruppe verwenden , um das Wort "Welt" zu erhalten und anschließend alles wie folgt abzurufen

(?:world).*

Die Beispielzeichenfolge wird hier getestet

Tadgh
quelle
28
Einige Leute, die mit einem Problem konfrontiert sind, denken: "Ich weiß, ich benutze einen regulären Ausdruck." ... jetzt haben Sie 2 Probleme ...
Joran Beasley
2
haha, mein Fehler, ich dachte, dies sei Regex, also habe ich versucht, eine Regex-Antwort zu geben. Na ja, jetzt ist es da.
Tadgh
1
Es ist alles gut ... es ist sicherlich eine Möglichkeit, diese Katze zu häuten ... übertrieben für dieses Problem (imho)
Joran Beasley
Der nicht erfassende Gruppenlink zeigt nicht mehr auf das Richtige.
Apteryx
1
Für Interessierte. Hier ist der vollständige Coderesult = re.search(r"(?:world)(.*)", "hello python world , i'm a beginner ").group(1)
RaduS
5

Sie können dieses Paket namens "Teilzeichenfolge" verwenden. Geben Sie einfach "pip install substring" ein. Sie können den Teilstring erhalten, indem Sie nur die Start- und Endzeichen / -indizes erwähnen.

Beispielsweise:

import substring

s = substring.substringByChar("abcdefghijklmnop", startChar="d", endChar="n")

print(s)

Ausgabe:

s = defghijklmn

Sriram Veturi
quelle
3

Es ist eine alte Frage, aber ich sah mich einem sehr ähnlichen Szenario gegenüber. Ich muss eine Zeichenfolge teilen, indem ich als Entmiliter das Wort "niedrig" verwende. Das Problem für mich war, dass ich in derselben Zeichenfolge das Wort unten und unten habe.

Ich habe es mit dem re-Modul auf diese Weise gelöst

import re

string = '...below...as higher prices mean lower demand to be expected. Generally, a high reading is seen as negative (or bearish), while a low reading is seen as positive (or bullish) for the Korean Won.'

Verwenden Sie re.split mit Regex, um das genaue Wort zu finden

stringafterword = re.split('\\blow\\b',string)[-1]
print(stringafterword)
' reading is seen as positive (or bullish) for the Korean Won.'

Der generische Code lautet:

re.split('\\bTHE_WORD_YOU_WANT\\b',string)[-1]

Hoffe das kann jemandem helfen!

Leonardo Hermoso
quelle
1
Vielleicht könnten Sie auch einfach verwenden : string.partition(" low ")[2]? (Beachten Sie die Leerzeichen auf beiden Seiten vonlow
Mtl Dev
1

Versuchen Sie diesen allgemeinen Ansatz:

import re
my_string="hello python world , i'm a beginner "
p = re.compile("world(.*)")
print (p.findall(my_string))

#[" , i'm a beginner "]
Hadij
quelle