Como obter uma string após uma substring específica?

226

Como posso obter uma string após uma substring específica?

Por exemplo, quero obter a sequência depois "world"demy_string="hello python world , i'm a beginner "

havox
fonte

Respostas:

399

A maneira mais fácil é provavelmente dividir a palavra-alvo

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

split usa a palavra (ou caractere) para dividir e, opcionalmente, um limite para o número de divisões.

Neste exemplo, divida em "mundo" e limite-o a apenas uma divisão.

Joran Beasley
fonte
Se eu precisar dividir um texto com a palavra 'baixa' e ela contiver a palavra menor antes, isso não funcionará!
Leonardo Hermoso 12/01
1
você simplesmente dividiria 2xtarget.split('lower',1)[-1].split('low',1)[-1]
Joran Beasley
e se a frase fosse "olá mundo megaworld python, eu sou iniciante". Como posso fazer parecer a palavra inteira e não fazer parte de outra como 'Megaworld'? Graças
pbou
1
em seguida, a seqüência que você busca é "mundo" ... ou o uso regex para boundrys palavra
Joran Beasley
6
my_string.partition("world")[-1](ou ...[2]) é mais rápido.
Martijn Pieters
66
s1 = "hello python world , i'm a beginner "
s2 = "world"

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

Se você deseja lidar com o caso em que nãos2 está presente , use o oposto de . Se o valor de retorno dessa chamada for , ele não estará .s1s1.find(s2)index-1s2s1

arshajii
fonte
você começa id distinta da (que são separadas por vários milhares) ... eu não tenho certeza que você não criar substrings desnecessários com este
Joran Beasley
@JoranBeasley, chamamos apenas index (), len () e slice. Não há razão para index () e len () criarem substrings, e se eles crêem (acho difícil de acreditar), isso é apenas um detalhe de implementação desnecessário. O mesmo para a fatia - não há razão para criar substrings diferentes daquele retornado.
shx2 8/11/19
@ shx2print( s1[s1.index(s2) + len(s2):] is s1[s1.index(s2) + len(s2):])
Joran Beasley
@JoranBeasley, que ponto você está tentando fazer com este trecho? Que em várias chamadas objetos diferentes são retornados? Por "substrings desnecessários", entendo substrings diferentes do retornado, ou seja, substrings que não são necessários para criar a fim de obter o resultado.
shx2 9/11/19
57

Estou surpreso que ninguém tenha mencionado partition.

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

IMHO, esta solução é mais legível que a do @ arshajii. Fora isso, acho que o @ arshajii's é o melhor por ser o mais rápido - ele não cria cópias / substrings desnecessárias.

shx2
fonte
2
Essa é uma boa solução e lida com o caso em que a substring não faz parte da cadeia de base de maneira adequada.
Mattmc3
você começa id distinta da (que são separadas por vários milhares) ... eu não tenho certeza que você não criar substrings desnecessários com este (e im com preguiça de perfil-lo corretamente)
Joran Beasley
1
@JoranBeasley, ele claramente faz criar substings desnecessários. Eu acho que você interpretou mal a minha resposta.
shx2 25/05/19
(o mesmo acontece do Arashi eu acho ...)
Joran Beasley
3
Além disso, isso é mais rápido que str.split(..., 1).
Martijn Pieters
20

Você quer usar str.partition():

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

porque esta opção é mais rápida que as alternativas .

Observe que isso produz uma cadeia vazia se o delimitador estiver ausente:

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

Se você deseja ter a sequência original, teste se o segundo valor retornado str.partition()não está vazio:

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

Você também pode usar str.split()com um limite de 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 "

No entanto, esta opção é mais lenta . Para um cenário de melhor caso, str.partition()é facilmente cerca de 15% mais rápido em comparação com 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%

Isso mostra os tempos por execução com entradas aqui, onde o delimitador está ausente (cenário de pior caso), colocado em primeiro lugar (cenário de melhor caso) ou na metade inferior, metade superior ou última posição. O tempo mais rápido é marcado com [...]e <...>marca o pior.

A tabela acima é produzida por um contra-relógio abrangente para as três opções, produzidas abaixo. Executei os testes no Python 3.7.4 em um Macbook Pro de 15 "modelo 2017 com Intel Core i7 a 2,9 GHz e ram de 16 GB.

Esse script gera sentenças aleatórias com e sem o delimitador selecionado aleatoriamente presente e, se presente, em diferentes posições da sentença gerada, executa os testes em ordem aleatória com repetições (produzindo resultados mais justos, contabilizando eventos aleatórios do SO ocorridos durante o teste), e depois imprime uma tabela dos resultados:

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
fonte
Ótima resposta! especialmente porque você fornecer a verdadeira razão é melhor: P
Joran Beasley
18

Se você quiser fazer isso usando regex, você pode simplesmente usar um grupo que não captura , para obter a palavra "mundo" e depois pegar tudo depois, assim

(?:world).*

A sequência de exemplo é testada aqui

Tadgh
fonte
28
algumas pessoas, quando confrontadas com um problema, pensam: "Eu sei, usarei uma expressão regular". ... agora você tem 2 problemas ...
Joran Beasley
2
haha, meu erro, eu pensei que isso estava marcado como regex, então tentei dar uma resposta regex. Oh, bem, está lá agora.
Tadgh 24/09/12
1
tudo é bom ... é certamente uma maneira de esfolar este gato ... um exagero para este problema embora (IMHO)
Joran Beasley
O link do grupo que não captura não está mais apontando para a coisa certa.
Apteryx
1
Para os interessados. Aqui está o código completoresult = re.search(r"(?:world)(.*)", "hello python world , i'm a beginner ").group(1)
RaduS 01/07/19
5

Você pode usar este pacote chamado "substring". Basta digitar "pip install substring". Você pode obter a substring apenas mencionando os caracteres / índices de início e fim.

Por exemplo:

import substring

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

print(s)

Resultado:

s = defghijklmn

Sriram Veturi
fonte
3

É uma pergunta antiga, mas enfrentei o mesmo cenário. Preciso dividir uma string usando como desmilitro a palavra "low". O problema para mim é que tenho na mesma string a palavra abaixo e abaixo.

Eu resolvi usando o módulo re desta maneira

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

use re.split com regex para corresponder à palavra exata

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

o código genérico é:

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

Espero que isso possa ajudar alguém!

Leonardo Hermoso
fonte
1
Talvez você também possa usar string.partition(" low ")[2]:? (Observe os espaços em ambos os lados dolow
Mtl Dev
1

Tente esta abordagem geral:

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
fonte