如何提取两个标记之间的子字符串?


335

假设我有一个字符串,'gfgfdAAA1234ZZZuijjk'而我只想提取'1234'一部分。

我只知道我感兴趣的部分之前AAA和之后ZZZ的几个字符1234

使用sed字符串可以执行以下操作:

echo "$STRING" | sed -e "s|.*AAA\(.*\)ZZZ.*|\1|"

结果,这会给我1234

如何在Python中做同样的事情?

Answers:


587

使用正则表达式- 文档以供进一步参考

import re

text = 'gfgfdAAA1234ZZZuijjk'

m = re.search('AAA(.+?)ZZZ', text)
if m:
    found = m.group(1)

# found: 1234

要么:

import re

text = 'gfgfdAAA1234ZZZuijjk'

try:
    found = re.search('AAA(.+?)ZZZ', text).group(1)
except AttributeError:
    # AAA, ZZZ not found in the original string
    found = '' # apply your error handling

# found: 1234

20
如果模式在大多数情况下都匹配,则第二种解决方案更好,因为它比请求更容易获得宽恕。
Bengt

7
索引不是从0开始吗?因此,您将需要使用组(0)而不是组(1)?
亚历山大

22
@Alexander,否,group(0)将返回完全匹配的字符串:AAA1234ZZZ,而group(1)将仅返回与第一个组匹配的字符:1234
Yurii K

1
@Bengt:为什么?第一种解决方案对我来说似乎很简单,并且具有更少的代码行。
HelloGoodbye '16

5
在这个表达式中?将+修改为非贪婪,即 它将匹配从1到任意次数的次数,但次数尽可能少,只会根据需要扩展。如果没有?,则第一组会将gfgfAAA2ZZZkeAAA43ZZZonife匹配为2ZZZkeAAA43,但带有?它只会匹配2,然后搜索多个(或将其删除并再次搜索)将匹配
Dom

113
>>> s = 'gfgfdAAA1234ZZZuijjk'
>>> start = s.find('AAA') + 3
>>> end = s.find('ZZZ', start)
>>> s[start:end]
'1234'

然后,您也可以在re模块中使用正则表达式,如果需要的话,但这不是必需的。


9
问题似乎暗示输入文本将始终同时包含“ AAA”和“ ZZZ”。如果不是这种情况,您的答案将严重失败(这意味着它返回的是完全错误的内容,而不是空字符串或引发异常;以“ hello there”为输入字符串)。
tzot 2011年

@ user225312 re方法不是更快吗?
confused00

1
投票,但为了可维护性,我将使用“ x ='AAA'; s.find(x)+ len(x)”而不是“ s.find('AAA')+ 3”。
Alex

1
如果在中找不到任何令牌ss.find将返回-1。切片运算符s[begin:end] 将其接受为有效索引,并返回不希望的子字符串。
ribamar

@ confused00发现是速度远远高于重新stackoverflow.com/questions/4901523/...
克劳迪乌Creanga

65

正则表达式

import re

re.search(r"(?<=AAA).*?(?=ZZZ)", your_text).group(0)

如果原样AttributeError中没有“ AAA”和“ ZZZ”,则上述原样会失败your_text

字符串方法

your_text.partition("AAA")[2].partition("ZZZ")[0]

如果中不存在“ AAA”或“ ZZZ”,则上面的内容将返回一个空字符串your_text

PS Python挑战?


6
这个答案可能值得更多投票。字符串方法是最可靠的方法。它不需要try / except。
ChaimG

不错,虽然有限。分区不是基于正则表达式的,所以它仅在这种情况下有效,因为搜索字符串受固定文字限制
GreenAsJade

太好了,非常感谢!-这适用于字符串,不需要正则表达式
Alex

我的天啊!真的,分区!非常感谢!
Andrey Wal

15
import re
print re.search('AAA(.*?)ZZZ', 'gfgfdAAA1234ZZZuijjk').group(1)

1
AttributeError: 'NoneType' object has no attribute 'groups'-如果字符串中没有AAA,则ZZZ ...
eumiro 2011年

12

惊讶的是没有人提到这是我一次性脚本的快速版本:

>>> x = 'gfgfdAAA1234ZZZuijjk'
>>> x.split('AAA')[1].split('ZZZ')[0]
'1234'

@ user1810100基本上说,您发布此消息的前一天几乎是5年……
John


7

您可以使用re模块:

>>> import re
>>> re.compile(".*AAA(.*)ZZZ.*").match("gfgfdAAA1234ZZZuijjk").groups()
('1234,)

5

使用sed可以用字符串执行以下操作:

echo "$STRING" | sed -e "s|.*AAA\(.*\)ZZZ.*|\1|"

结果是我会得到1234。

您可以re.sub使用相同的正则表达式对函数执行相同的操作。

>>> re.sub(r'.*AAA(.*)ZZZ.*', r'\1', 'gfgfdAAA1234ZZZuijjk')
'1234'

在基本sed中,捕获组由表示\(..\),但是在python中,捕获组由表示(..)


5

在python中,可以使用findall正则表达式(re)模块中的方法来提取子字符串形式的字符串。

>>> import re
>>> s = 'gfgfdAAA1234ZZZuijjk'
>>> ss = re.findall('AAA(.+)ZZZ', s)
>>> print ss
['1234']

4

您可以在代码中找到此功能的第一个子字符串(按字符索引)。另外,您可以找到子字符串之后的内容。

def FindSubString(strText, strSubString, Offset=None):
    try:
        Start = strText.find(strSubString)
        if Start == -1:
            return -1 # Not Found
        else:
            if Offset == None:
                Result = strText[Start+len(strSubString):]
            elif Offset == 0:
                return Start
            else:
                AfterSubString = Start+len(strSubString)
                Result = strText[AfterSubString:AfterSubString + int(Offset)]
            return Result
    except:
        return -1

# Example:

Text = "Thanks for contributing an answer to Stack Overflow!"
subText = "to"

print("Start of first substring in a text:")
start = FindSubString(Text, subText, 0)
print(start); print("")

print("Exact substring in a text:")
print(Text[start:start+len(subText)]); print("")

print("What is after substring \"%s\"?" %(subText))
print(FindSubString(Text, subText))

# Your answer:

Text = "gfgfdAAA1234ZZZuijjk"
subText1 = "AAA"
subText2 = "ZZZ"

AfterText1 = FindSubString(Text, subText1, 0) + len(subText1)
BeforText2 = FindSubString(Text, subText2, 0) 

print("\nYour answer:\n%s" %(Text[AfterText1:BeforText2]))

3
>>> s = '/tmp/10508.constantstring'
>>> s.split('/tmp/')[1].split('constantstring')[0].strip('.')

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

print(text[text.index(left)+len(left):text.index(right)])

string

2

以防万一某人必须做与我相同的事情。我必须在一行中提取括号内的所有内容。例如,如果我有一条类似“美国总统(巴拉克·奥巴马)与...会面……”这样的句子,而我只想获得“巴拉克·奥巴马”,这就是解决方案:

regex = '.*\((.*?)\).*'
matches = re.search(regex, line)
line = matches.group(1) + '\n'

即您需要用slash \符号来阻止括号。虽然这是关于Python的更多正则表达式的问题。

另外,在某些情况下,您可能会在正则表达式定义之前看到“ r”符号。如果没有r前缀,则需要像C中那样使用转义符。这里有更多讨论。


2

使用PyParsing

import pyparsing as pp

word = pp.Word(pp.alphanums)

s = 'gfgfdAAA1234ZZZuijjk'
rule = pp.nestedExpr('AAA', 'ZZZ')
for match in rule.searchString(s):
    print(match)

产生:

[['1234']]


0

这是一个不使用正则表达式的解决方案,它也解决了第一个子字符串包含第二个子字符串的情况。仅当第二个标记在第一个标记之后时,此函数才会找到子字符串。

def find_substring(string, start, end):
    len_until_end_of_first_match = string.find(start) + len(start)
    after_start = string[len_until_end_of_first_match:]
    return string[string.find(start) + len(start):len_until_end_of_first_match + after_start.find(end)]

0

另一种方法是使用列表(假设您要查找的子字符串仅由数字组成):

string = 'gfgfdAAA1234ZZZuijjk'
numbersList = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
output = []

for char in string:
    if char in numbersList: output.append(char)

print(f"output: {''.join(output)}")
### output: 1234

-1

如果没有匹配项,一个衬里返回其他字符串。编辑:改进的版本使用next功能,"not-found"如果需要,请替换为其他内容:

import re
res = next( (m.group(1) for m in [re.search("AAA(.*?)ZZZ", "gfgfdAAA1234ZZZuijjk" ),] if m), "not-found" )

我执行此操作的另一种方法(不太理想)第二次使用正则表达式,但仍未找到更短的方法:

import re
res = ( ( re.search("AAA(.*?)ZZZ", "gfgfdAAA1234ZZZuijjk") or re.search("()","") ).group(1) )
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.