Come ottenere una stringa dopo una sottostringa specifica?


226

Come posso ottenere una stringa dopo una sottostringa specifica?

Ad esempio, voglio ottenere la stringa dopo "world"inmy_string="hello python world , i'm a beginner "

Risposte:


399

Il modo più semplice è probabilmente quello di dividere la parola target

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

split prende la parola (o il carattere) su cui dividere e facoltativamente un limite al numero di split.

In questo esempio dividi "mondo" e limitalo a una sola divisione.


Se devo dividere un testo con la parola "bassa" e contiene la parola in basso prima di essa, questo non funzionerà!
Leonardo Hermoso,

1
divideresti semplicemente 2xtarget.split('lower',1)[-1].split('low',1)[-1]
Joran Beasley

e se la frase fosse "ciao mondo Python Megaworld, io sono un principiante". Come posso far sembrare l'intera parola e non parte di un'altra come "Megaworld"? Grazie
pbou il

1
allora la stringa che cerchi è "mondo" ... o usa regex per la parola boundrys
Joran Beasley,

6
my_string.partition("world")[-1](o ...[2]) è più veloce.
Martijn Pieters

66
s1 = "hello python world , i'm a beginner "
s2 = "world"

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

Se si desidera affrontare il caso in cui nons2 è presente , utilizzare al contrario . Se il valore restituito di quella chiamata è , allora non è in .s1s1.find(s2)index-1s2s1


ottieni identificativi distinti (che sono separati da diverse migliaia) ... non sono sicuro di non creare sottostringhe inutili con questo
Joran Beasley,

@JoranBeasley, chiamiamo solo index (), len () e slice. Non c'è motivo per index () e len () di creare sottostringhe, e se lo fanno (trovo difficile da credere), questo è solo un dettaglio di implementazione non necessario. Lo stesso vale per lo slice: non esiste alcun motivo per creare sottostringhe diverse da quella restituita.
shx2,

@ shx2print( s1[s1.index(s2) + len(s2):] is s1[s1.index(s2) + len(s2):])
Joran Beasley,

@JoranBeasley che punto stai cercando di fare con questo frammento? Che su più chiamate vengono restituiti oggetti diversi? per "sottostringhe non necessarie" intendo sottostringhe diverse da quella restituita, ovvero sottostringhe che non sono necessarie per creare il risultato.
shx2,

57

Sono sorpreso che nessuno abbia menzionato partition.

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

IMHO, questa soluzione è più leggibile di quella di @ arshajii. A parte questo, penso che @ arshajii sia il migliore per essere il più veloce - non crea copie / sottostringhe non necessarie.


2
Questa è una buona soluzione e gestisce il caso in cui la sottostringa non fa bene parte della stringa di base.
mattmc3,

ottieni identificativi distinti (che sono separati da diverse migliaia) ... non sono sicuro di non creare sottostringhe inutili con questo (e sono troppo pigro per profilarlo correttamente)
Joran Beasley,

1
@JoranBeasley, chiaramente non creare substings inutili. Penso che tu abbia letto male la mia risposta.
shx2,

(così fa Arashi, credo ...)
Joran Beasley,

3
Inoltre, questo è più veloce di str.split(..., 1).
Martijn Pieters

20

Vuoi usare str.partition():

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

perché questa opzione è più veloce delle alternative .

Si noti che questo produce una stringa vuota se manca il delimitatore:

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

Se desideri avere la stringa originale, verifica se il secondo valore restituito str.partition()non è vuoto:

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

Puoi anche usare str.split()con un limite di 1:

>>> 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 "

Tuttavia, questa opzione è più lenta . Per una migliore dei casi, str.partition()è facilmente circa il 15% più veloce rispetto a str.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%

Questo mostra i tempi per esecuzione con input qui il delimitatore è mancante (scenario peggiore), posizionato per primo (scenario migliore) o nella metà inferiore, metà superiore o ultima posizione. Il tempo più veloce è contrassegnato con [...]e <...>segna il peggio.

La tabella sopra è prodotta da una completa cronometro per tutte e tre le opzioni, prodotta di seguito. Ho eseguito i test su Python 3.7.4 su un Macbook Pro 15 "modello 2017 da 2017 con Intel Core i7 da 2,9 GHz e ram da 16 GB.

Questo script genera frasi casuali con e senza il delimitatore selezionato casualmente presente e, se presente, in diverse posizioni nella frase generata, esegue i test in ordine casuale con ripetizioni (producendo i risultati più equi tenendo conto degli eventi casuali del sistema operativo che si verificano durante il test), e quindi stampa una tabella dei risultati:

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="  ")

Bella risposta! soprattutto perché fornisci il vero motivo per cui è meglio: P
Joran Beasley

18

Se vuoi farlo usando regex, potresti semplicemente usare un gruppo non di acquisizione , per ottenere la parola "mondo" e poi prendere tutto dopo, in questo modo

(?:world).*

La stringa di esempio viene testata qui


28
alcune persone di fronte a un problema pensano "Lo so, Ill usare un'espressione regolare". ... ora hai 2 problemi ...
Joran Beasley,

2
ahah, errore mio, ho pensato che fosse taggato regex quindi ho cercato di dare una risposta regex. Oh bene, è lì adesso.
Tadgh,

1
è tutto buono ... è certamente un modo di scuoiare questo gatto ... eccessivo per questo problema (imho)
Joran Beasley,

Il collegamento di gruppo non acquisito non punta più alla cosa giusta.
Apteryx,

1
Per chi è interessato. Ecco il codice completoresult = re.search(r"(?:world)(.*)", "hello python world , i'm a beginner ").group(1)
RaduS

5

È possibile utilizzare questo pacchetto chiamato "sottostringa". Basta digitare "pip install substring". È possibile ottenere la sottostringa citando solo i caratteri / indici di inizio e fine.

Per esempio:

import substring

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

print(s)

Produzione:

s = defghijklmn


3

È una vecchia domanda ma ho affrontato lo stesso scenario, ho bisogno di dividere una stringa usando come demiliter la parola "basso" il problema per me era che ho nella stessa stringa la parola sotto e sotto.

L'ho risolto usando il modulo re in questo modo

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.'

usa re.split con regex per abbinare la parola esatta

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

il codice generico è:

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

Spero che questo possa aiutare qualcuno!


1
Forse si potrebbe anche semplicemente usare: string.partition(" low ")[2]? (Nota gli spazi su entrambi i lati dilow
Mtl Dev

1

Prova questo approccio generale:

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

#[" , i'm a beginner "]

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.