在两个子字符串之间查找字符串

397

如何找到两个子字符串之间的字符串 ('123STRINGabc' -> 'STRING')?

我目前的方法如下:

>>> start = 'asdf=5;'
>>> end = '123jasd'
>>> s = 'asdf=5;iwantthis123jasd'
>>> print((s.split(start))[1].split(end)[0])
iwantthis

然而,这种方法似乎非常低效和不符合Python风格。有没有更好的方法来处理类似的情况?

忘了提一下: 字符串可能不以startend开始和结束。它们之前和之后可能有更多字符。


2
您提供的额外信息使得使用正则表达式以达到最大正确性几乎成为必要。 - Jesse Dhillon
32
你的解决方案有什么问题吗?实际上,我更喜欢你的解决方案而不是你接受的那个。 - reubano
我也试图这样做,但对于多个实例,似乎使用*?进行非贪婪搜索,然后仅使用s[s.find(end)]截断字符串可用于跟踪多个实例。 - lathomas64
1
@reubano:这段代码存在一个功能/错误,即当结束文本不出现在原始文本中时,它不会引发异常。 接受的答案修复了这个问题。 - Kasper Dokter
只是一个提示:s[1:-1]也可以做你想要的事情...虽然我更喜欢下面的.group(1)(.*?)非贪婪。 - alchemy
20个回答

503
import re

s = 'asdf=5;iwantthis123jasd'
result = re.search('asdf=5;(.*)123jasd', s)
print(result.group(1))

1
@Jesse Dhillon -- 你觉得 @Tim McNamara 的建议 ''.join(start,test,end) in a_string 怎么样? - jdd
这种方法更短,类似于JavaScript的方法。 - leonneo
7
如果我需要在两个子字符串之间进行查找,且第二个子字符串在第一个子字符串之后重复出现,该如何处理呢?例如: s = 'asdf=5;I_WANT_ONLY_THIS123jasdNOT_THIS123jasd' - user5713018
8
? 添加到正则表达式中,使其变成非贪婪匹配:result = re.search('asdf=5;(.*?)(?=123jasd)', s) - do-ic
1
如果开始/结束重复,该如何修改以选择开始/结束之间的数据?例如,假设我想在<>之间分别选择两个字符串“i would like to send <message> to <name>”,并返回result1='message'result2='name' - Sql_Pete_Belfast
显示剩余9条评论

185
s = "123123STRINGabcabc"

def find_between( s, first, last ):
    try:
        start = s.index( first ) + len( first )
        end = s.index( last, start )
        return s[start:end]
    except ValueError:
        return ""

def find_between_r( s, first, last ):
    try:
        start = s.rindex( first ) + len( first )
        end = s.rindex( last, start )
        return s[start:end]
    except ValueError:
        return ""


print find_between( s, "123", "abc" )
print find_between_r( s, "123", "abc" )

产生:

123STRING
STRINGabc

我认为需要注意的是,根据您所需要的行为,您可以混合使用indexrindex调用,或者选择以上某个版本(相当于正则表达式中的(.*)(.*?)组)。


46
他说他想要一种更符合Python风格的方式,但这明显不是。我不确定为什么选择了这个答案,即使原帖的解决方案更好。 - Jesse Dhillon
2
同意。我会使用@Tim McNamara的解决方案,或者使用他的建议,例如start+test+end in substring - jdd
好的,那么它不太符合Python的风格,是吗?它比正则表达式也不那么高效吗?还有@Prabhu的答案需要您点踩,因为它提供了相同的解决方案。 - cji
1
+1,对于更通用和可重复使用(通过导入)的解决方案。 - Ida
3
如果end出现多次,那么此解决方案比其他解决方案更为有效,因此我会给它加上一个赞(+1)。但我也同意原帖作者的解决方案更简单。 - reubano
显示剩余2条评论

140
start = 'asdf=5;'
end = '123jasd'
s = 'asdf=5;iwantthis123jasd'
print s[s.find(start)+len(start):s.rfind(end)]

给予

iwantthis

5
我点赞了这个,因为它可以适用于任何输入字符串的大小。其他一些方法则假定您事先知道长度。 - Kenny Powers
2
是的,它可以在没有输入大小的情况下工作,但它假定字符串存在。 - Kevin Crum
这个函数会提取第二个字符串的第一次出现和最后一次出现之间的内容,这可能是错误的,特别是在解析HTML时。不幸的是,这个问题似乎已经关闭了,所以我无法发布我的答案。 - Lenka Pitonakova
然而,这个方法提取的是第一个和最后一个出现的第二个字符串之间的内容,这可能是不正确的,尤其是在解析HTML时。不幸的是,这个问题似乎已经关闭,所以我无法发布我的答案。 - undefined

63
s[len(start):-len(end)]

14
假设开头和结尾总是在字符串的开头和结尾,那么这很不错。否则,我可能会使用正则表达式。 - jdd
3
我想到了最符合Python风格的答案来回答原问题。使用in运算符进行测试可能比正则表达式更快。 - Tim McNamara

44
只是将原帖作者的解决方案转化为答案而已:
def find_between(s, start, end):
    return s.split(start)[1].split(end)[0]

12
如果你将别人的解决方案当作自己的,那么最好将它设为社区维基页面。 - David Arenburg

39

字符串格式化使得Nikolaus Gradwohl所提出的建议更加灵活。可以根据需要修改 start end

import re

s = 'asdf=5;iwantthis123jasd'
start = 'asdf=5;'
end = '123jasd'

result = re.search('%s(.*)%s' % (start, end), s).group(1)
print(result)

3
我得到了这个错误:'NoneType'对象没有属性'group' - Dentrax
1
这意味着没有找到匹配项。请检查您的正则表达式。 - Tim McNamara
@Dentrax 是正确的:应该返回空,而不是错误。 - cwhisperer
我认为Tim的意思是搜索应该返回None,因为没有匹配项。由于搜索返回了“None”,在最后应用.group(1)会导致错误。 - MTay

31

如果你不想导入任何东西,试试字符串方法.index()

text = 'I want to find a string between two substrings'
left = 'find a '
right = 'between two'

# Output: 'string'
print(text[text.index(left)+len(left):text.index(right)])

4
我很喜欢它。简单、单行、清晰易懂,无需额外导入即可立即使用。我不知道上面那些过度工程化的答案是怎么回事。 - PaulB
1
这并不检查“正确”的文本是否实际上在文本的右侧。如果在文本之前有任何“正确”的出现,它将无法工作。 - AndreFeijo
1
@AndreFeijo 我同意你的观点,这也是我在尝试提取文本时的第一种解决方案,因为我想避免使用正则表达式的奇怪语法。然而,在像你提到的情况下,我会使用正则表达式。 - Fernando Wittmann
在这种情况下(不是所有情况),你可以找到左边然后右边,尽管这只是两行代码: text = text[text.index(left)+len(left):len(role)] text = text[0:text.index(right)] - ericksho
嗨,Fernando,针对这段文本“ADRIANOPICCININIC216186162022-07-27 09:36:33Z”,我想提取的只有“C21618616”,该如何做到? - Arun Mohan

16
source='your token _here0@df and maybe _here1@df or maybe _here2@df'
start_sep='_'
end_sep='@df'
result=[]
tmp=source.split(start_sep)
for par in tmp:
  if end_sep in par:
    result.append(par.split(end_sep)[0])

print result

必须展示: here0,here1,here2

正则表达式更好,但它需要额外的库,您可能想要仅使用Python。


这对我有用。感谢您将解决方案扩展到多个出现次数。 - Sterex
1
我正好在找这个,它有助于多个出现的情况,这篇文章需要更多的赞 :p。 - ohsoifelse

15

以下是一种方法

_,_,rest = s.partition(start)
result,_,_ = rest.partition(end)
print result

另一种使用正则表达式的方法

import re
print re.findall(re.escape(start)+"(.*)"+re.escape(end),s)[0]
或者
print re.search(re.escape(start)+"(.*)"+re.escape(end),s).group(1)

6

这里是我编写的一个函数,用于返回一个列表,其中包含在搜索 string1 和 string2 之间的字符串。

def GetListOfSubstrings(stringSubject,string1,string2):
    MyList = []
    intstart=0
    strlength=len(stringSubject)
    continueloop = 1

    while(intstart < strlength and continueloop == 1):
        intindex1=stringSubject.find(string1,intstart)
        if(intindex1 != -1): #The substring was found, lets proceed
            intindex1 = intindex1+len(string1)
            intindex2 = stringSubject.find(string2,intindex1)
            if(intindex2 != -1):
                subsequence=stringSubject[intindex1:intindex2]
                MyList.append(subsequence)
                intstart=intindex2+len(string2)
            else:
                continueloop=0
        else:
            continueloop=0
    return MyList


#Usage Example
mystring="s123y123o123pp123y6"
List = GetListOfSubstrings(mystring,"1","y68")
for x in range(0, len(List)):
               print(List[x])
output:


mystring="s123y123o123pp123y6"
List = GetListOfSubstrings(mystring,"1","3")
for x in range(0, len(List)):
              print(List[x])
output:
    2
    2
    2
    2

mystring="s123y123o123pp123y6"
List = GetListOfSubstrings(mystring,"1","y")
for x in range(0, len(List)):
               print(List[x])
output:
23
23o123pp123

非常好的和有用的回答。谢谢! - ibarant
非常出色的回答。我会雇用像你这样的人。 - Abhishek Singh

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