如何在特定子字符串后获取一个字符串?

367

如何获取特定子字符串后的字符串?

例如,我想要在以下字符串中获取"world"后面的字符串:

my_string="hello python world, I'm a beginner"

在这种情况下,输出为:",我是一个初学者"

10个回答

600

最简单的方法可能就是在目标词上进行分割

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

split函数接受一个单词(或字符)作为分隔符,并可选地指定拆分的次数上限。

在此示例中,使用“world”作为分隔符并将其限制为仅拆分一次。


2
如果我需要使用“low”单词来分割文本,并且在它之前包含单词“lower”,那么这种方法将无法正常工作! - Leonardo Hermoso
3
你只需要将2x拆分成两部分并提取中间的内容,方法是使用'target.split('lower',1)[-1].split('low',1)[-1]'。 - Joran Beasley
如果句子是“hello python Megaworld world, i'm a beginner”,我该如何让它查找整个单词而不是像'Megaworld'这样的部分单词?谢谢。 - pbou
1
如果您要搜索的字符串是“world”...或者使用正则表达式进行单词边界匹配。 - Joran Beasley
17
my_string.partition("world")[-1](或 ...[2])更快。 - Martijn Pieters
显示剩余3条评论

98

我很惊讶没有人提到 partition

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

s1="hello python world, I'm a beginner"
substring_after(s1, "world")

# ", I'm a beginner"

依我之见,这种解决方案比@arshajii的更易读。除此之外,我认为@arshajii的是最快的,因为它不会创建任何不必要的副本/子字符串。


2
这是一个不错的解决方案,而且很好地处理了子字符串不是基础字符串一部分的情况。 - mattmc3
你会得到不同的ID(它们相隔数千),我不确定你是否会因此创建不必要的子字符串(而我太懒了,无法正确地对其进行分析)。 - Joran Beasley
1
@JoranBeasley,它明显会创建不必要的子字符串。我认为你误读了我的回答。 - shx2
(我认为嵐也是这样的...) - Joran Beasley
4
此外,这比 str.split(..., 1) 更快。 - Martijn Pieters
该死,每当我认为我已经学会了 Python 的所有内容时,我总能发现新的有趣事情。 - urek mazino

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

print(s1[s1.index(s2) + len(s2):])
如果你想处理 s1 中没有出现 s2 的情况,那么请使用 s1.find(s2) 而不是 index。如果该调用的返回值为 -1,则表示 s1 中没有 s2

你会得到不同的 ID(它们相隔几千),我不确定你是否会创建不必要的子字符串。 - Joran Beasley
@JoranBeasley,我们只调用index()、len()和slice。没有理由让index()和len()创建子字符串,如果它们这样做了(我很难相信),那只是一个不必要的实现细节。对于slice也是一样--除了返回的子字符串之外,没有理由让它创建其他子字符串。 - shx2
@shx2 print(s1[s1.index(s2) + len(s2):] is s1[s1.index(s2) + len(s2):]) - Joran Beasley
@JoranBeasley,您在这段代码片段中想要表达什么意思?是指多次调用会返回不同的对象吗?“不必要的子字符串”是指除了返回的子字符串之外的其他子字符串,即为了得出结果而不必要创建的子字符串。 - shx2

61

您想要使用str.partition()函数:

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

因为这个选项比其他选项更快
请注意,如果分隔符缺失,则会生成一个空字符串:
>>> my_string.partition("Monty")[2]  # delimiter missing
''

如果你想要原始的字符串,那么可以通过测试str.partition()返回的第二个值是否为空来实现:
prefix, success, result = my_string.partition(delimiter)
if not success: result = prefix

你也可以使用 str.split() 并且设置一个限制为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 "

然而,这个选项会比较。在最理想的情况下,str.partition()str.split() 快约 15%

                                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%

这显示了每次执行的时间,输入时分隔符可能缺失(最坏情况),放在第一位(最佳情况),或者在下半部分、上半部分或最后位置。最快的时间用 [...] 标记,<...> 表示最差。
上述表格是通过对所有三个选项进行全面的时间试验生成的,如下所示。我在2017款15英寸Macbook Pro上以2.9 GHz Intel Core i7和16 GB内存运行Python 3.7.4进行测试。
该脚本生成带有随机选择的分隔符的随机句子,并在生成的句子中不同位置(如果存在)运行重复的随机顺序测试(考虑到测试期间发生的随机操作系统事件,产生公正的结果),然后打印结果表格:
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="  ")

1
好答案!特别是因为你提供了真正的原因,这样更好 :P - Joran Beasley

24
如果您想使用正则表达式来完成此操作,可以简单地使用非捕获组获取单词“world”,然后获取其后的所有内容,例如:
(?:world).*

这个示例字符串在这里进行了测试


35
有些人遇到问题时会想:“我知道,我可以使用正则表达式。”...现在你有了两个问题... - Joran Beasley
3
哈哈,我的错误,我以为这个标签是正则表达式,所以试图给出一个正则表达式的答案。好吧,现在已经有了。 - Tadgh
2
都很好... 这是解决问题的一种方式... 但是对于这个问题来说有点过度了(我个人认为)。 - Joran Beasley
非捕获组链接不再指向正确的对象。 - Apteryx
我认为使用正则表达式有点多余,但就在昨天,我正在寻找一个用正则表达式表示的答案,尽管我知道可以用更有效率的方式完成。我试图学习更多关于正则表达式的知识。 - Bobort
4
对于有兴趣的人,以下是完整的代码:result = re.search(r"(?:world)(.*)", "hello python world , i'm a beginner ").group(1) - RaduS

10

我本打算添加这个,但是找到了这个答案。 - Brian

7
您可以使用名为substring的包。只需使用命令pip install substring进行安装。您可以通过指定起始和结束字符/索引来获取子字符串。

例如:

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

输出:

# s = defghijklmn

6

这是一个老问题,但我遇到了一个非常相似的情况,我需要使用单词“low”作为分隔符来拆分字符串。对我而言,问题在于同一字符串中包含有以下单词:below和lower。

我用re模块解决了这个问题,方法如下:

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 with regex to match the exact word
stringafterword = re.split('\\blow\\b',string)[-1]

print(stringafterword)
# ' reading is seen as positive (or bullish) for the Korean Won.'

# the generic code is:
re.split('\\bTHE_WORD_YOU_WANT\\b',string)[-1]

希望这能帮助到某些人!

1
也许你可以直接使用:string.partition(" low ")[2]?(注意low两侧的空格) - Mtl Dev

6

尝试采用以下通用方法:

import re

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

# [" , i'm a beginner "]

0

如果您更喜欢只使用 Python 正则表达式 re 库来完成此操作,您可以使用 Match.string 属性和 Match.end() 方法来操作 Match 对象:

import re

my_string="hello python world, I'm a beginner"

match = re.search("world", my_string)

if match:
    print(match.string[match.end():])
    # , I'm a beginner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接