¿Cómo obtener una cadena después de una subcadena específica?

226

¿Cómo puedo obtener una cadena después de una subcadena específica?

Por ejemplo, quiero obtener la cadena después "world"demy_string="hello python world , i'm a beginner "

havox
fuente

Respuestas:

399

La forma más fácil es probablemente dividir su palabra objetivo

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

dividir toma la palabra (o carácter) para dividir y, opcionalmente, un límite para el número de divisiones.

En este ejemplo, divida en "mundo" y limítelo a una sola división.

Joran Beasley
fuente
Si necesito dividir un texto con la palabra 'bajo' y contiene la palabra más abajo, ¡esto no funcionará!
Leonardo Hermoso
1
simplemente dividirías 2xtarget.split('lower',1)[-1].split('low',1)[-1]
Joran Beasley
¿Qué pasa si la frase era "Hola mundo de Python Megaworld, soy un principiante". ¿Cómo puedo hacer que se vea la palabra completa y no parte de otra como 'Megaworld'? Gracias
pbou
1
entonces la cadena que buscas es "mundo" ... o usa expresiones regulares para palabras clave
Joran Beasley
66
my_string.partition("world")[-1](o ...[2]) es más rápido.
Martijn Pieters
66
s1 = "hello python world , i'm a beginner "
s2 = "world"

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

Si desea tratar el caso en el s2que no está presente s1, utilice s1.find(s2)en lugar de index. Si el valor de retorno de esa llamada es -1, entonces s2no está en s1.

arshajii
fuente
obtienes identificaciones distintas (que están separadas por varios miles) ... no estoy seguro de que no crees subcadenas innecesarias con esto
Joran Beasley
@JoranBeasley, solo llamamos index (), len () y slice. No hay ninguna razón para que index () y len () creen subcadenas, y si lo hacen (me resulta difícil de creer), eso es solo un detalle de implementación innecesario. Lo mismo para el segmento: no hay ninguna razón para que cree subcadenas que no sea la que se devuelve.
shx2
@ shx2print( s1[s1.index(s2) + len(s2):] is s1[s1.index(s2) + len(s2):])
Joran Beasley
@JoranBeasley, ¿qué punto estás tratando de hacer con este fragmento? Que en múltiples llamadas se devuelven diferentes objetos? por "subcadenas innecesarias" me refiero a subcadenas distintas de la que se devuelve, es decir, subcadenas que no es necesario crear para obtener el resultado.
shx2
57

Me sorprende que nadie lo haya mencionado partition.

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

En mi humilde opinión, esta solución es más legible que @ arshajii. Aparte de eso, creo que @ arshajii's es el mejor para ser el más rápido: no crea copias / subcadenas innecesarias.

shx2
fuente
2
Esta es una buena solución, y maneja el caso donde la subcadena no es parte de la cadena base muy bien.
mattmc3
obtienes identificaciones distintas (que están separadas por varios miles) ... no estoy seguro de que no crees subcadenas innecesarias con esto (y soy demasiado vago para perfilarlo correctamente)
Joran Beasley
1
@JoranBeasley, está claro que hace crear substings innecesarios. Creo que leíste mal mi respuesta.
shx2
(al igual que arashi, creo ...)
Joran Beasley
3
Además, esto es más rápido que str.split(..., 1).
Martijn Pieters
20

Quieres usar str.partition():

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

porque esta opción es más rápida que las alternativas .

Tenga en cuenta que esto produce una cadena vacía si falta el delimitador:

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

Si desea tener la cadena original, pruebe si el segundo valor devuelto str.partition()no está vacío:

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

También puede usar str.split()con un límite 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 "

Sin embargo, esta opción es más lenta . Para el mejor de los casos, str.partition()es fácilmente un 15% más rápido en comparación con 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%

Esto muestra los tiempos por ejecución con entradas aquí, falta el delimitador (peor de los casos), colocado primero (mejor de los casos), o en la mitad inferior, la mitad superior o la última posición. El tiempo más rápido está marcado con [...]y <...>marca el peor.

La tabla anterior se produce mediante una contrarreloj integral para las tres opciones, que se presenta a continuación. Ejecuté las pruebas en Python 3.7.4 en un modelo 2017 Macbook Pro de 15 "con Intel Core i7 a 2.9 GHz y 16 GB de RAM.

Este script genera oraciones aleatorias con y sin el delimitador seleccionado al azar presente, y si está presente, en diferentes posiciones en la oración generada, ejecuta las pruebas en orden aleatorio con repeticiones (produciendo los resultados más justos que representan los eventos aleatorios del sistema operativo que tienen lugar durante la prueba), y luego imprime una tabla de 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
fuente
¡gran respuesta! especialmente porque proporcionas la verdadera razón por la que esto es mejor: P
Joran Beasley
18

Si desea hacer esto usando expresiones regulares, simplemente puede usar un grupo que no captura , para obtener la palabra "mundo" y luego tomar todo después, como así

(?:world).*

La cadena de ejemplo se prueba aquí

Tadgh
fuente
28
Algunas personas cuando se enfrentan a un problema piensan "Lo sé, usaré una expresión regular". ... ahora tienes 2 problemas ...
Joran Beasley
2
jaja, mi error, pensé que esto estaba etiquetado regex, así que traté de dar una respuesta regex. Oh bueno, está ahí ahora.
Tadgh
1
todo está bien ... es sin duda una forma de desollar a este gato ... aunque exagerado para este problema (en mi humilde opinión)
Joran Beasley
El enlace del grupo que no captura ya no apunta a lo correcto.
Apteryx
1
Para los interesados Aquí está el código completoresult = re.search(r"(?:world)(.*)", "hello python world , i'm a beginner ").group(1)
RaduS 01 de
5

Puede usar este paquete llamado "subcadena". Simplemente escriba "pip install substring". Puede obtener la subcadena simplemente mencionando los caracteres / índices iniciales y finales.

Por ejemplo:

import substring

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

print(s)

Salida:

s = defghijklmn

Sriram Veturi
fuente
3

Es una vieja pregunta, pero me enfrenté al mismo escenario, necesito dividir una cadena usando como palabra "bajo", el problema para mí fue que tengo en la misma cadena la palabra de abajo y más abajo.

Lo resolví usando el módulo re de esta manera

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 con regex para que coincida con la palabra exacta

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

El código genérico es:

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

¡Espero que esto pueda ayudar a alguién!

Leonardo Hermoso
fuente
1
Quizás también podrías usar string.partition(" low ")[2]:? (Tenga en cuenta los espacios a cada lado delow
Mtl Dev
1

Pruebe este enfoque general:

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
fuente