How to extract the substring between two markers?

Let's say I have a string 'gfgfdAAA1234ZZZuijjk' and I want to extract just the '1234' part.

I only know what will be the few characters directly before AAA, and after ZZZ the part I am interested in 1234.

With sed it is possible to do something like this with a string:

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

And this will give me 1234 as a result.

How to do the same thing in Python?

1

20 Answers

Using regular expressions - documentation for further reference

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

or:

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 
12
>>> s = 'gfgfdAAA1234ZZZuijjk' >>> start = s.find('AAA') + 3 >>> end = s.find('ZZZ', start) >>> s[start:end] '1234' 

Then you can use regexps with the re module as well, if you want, but that's not necessary in your case.

5

regular expression

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

The above as-is will fail with an AttributeError if there are no "AAA" and "ZZZ" in your_text

string methods

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

The above will return an empty string if either "AAA" or "ZZZ" don't exist in your_text.

PS Python Challenge?

4

Surprised that nobody has mentioned this which is my quick version for one-off scripts:

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

you can do using just one line of code

>>> import re >>> re.findall(r'\d{1,5}','gfgfdAAA1234ZZZuijjk') >>> ['1234'] 

result will receive list...

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

You can use re module for that:

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

In python, extracting substring form string can be done using findall method in regular expression (re) module.

>>> import re >>> s = 'gfgfdAAA1234ZZZuijjk' >>> ss = re.findall('AAA(.+)ZZZ', s) >>> print ss ['1234'] 
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)]) 

Gives

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

With sed it is possible to do something like this with a string:

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

And this will give me 1234 as a result.

You could do the same with re.sub function using the same regex.

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

In basic sed, capturing group are represented by \(..\), but in python it was represented by (..).

One liner with Python 3.8:

text[text.find(start:='AAA')+len(start):text.find('ZZZ')] 
2

You can find first substring with this function in your code (by character index). Also, you can find what is after a substring.

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])) 

Using 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) 

which yields:

[['1234']]

Just in case somebody will have to do the same thing that I did. I had to extract everything inside parenthesis in a line. For example, if I have a line like 'US president (Barack Obama) met with ...' and I want to get only 'Barack Obama' this is solution:

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

I.e. you need to block parenthesis with slash \ sign. Though it is a problem about more regular expressions that Python.

Also, in some cases you may see 'r' symbols before regex definition. If there is no r prefix, you need to use escape characters like in C. Here is more discussion on that.

Here's a solution without regex that also accounts for scenarios where the first substring contains the second substring. This function will only find a substring if the second marker is after the first marker.

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)] 

Another way of doing it is using lists (supposing the substring you are looking for is made of numbers, only) :

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 

Typescript. Gets string in between two other strings.

Searches shortest string between prefixes and postfixes

prefixes - string / array of strings / null (means search from the start).

postfixes - string / array of strings / null (means search until the end).

public getStringInBetween(str: string, prefixes: string | string[] | null, postfixes: string | string[] | null): string { if (typeof prefixes === 'string') { prefixes = [prefixes]; } if (typeof postfixes === 'string') { postfixes = [postfixes]; } if (!str || str.length < 1) { throw new Error(str + ' should contain ' + prefixes); } let start = prefixes === null ? { pos: 0, sub: '' } : this.indexOf(str, prefixes); const end = postfixes === null ? { pos: str.length, sub: '' } : this.indexOf(str, postfixes, start.pos + start.sub.length); let value = str.substring(start.pos + start.sub.length, end.pos); if (!value || value.length < 1) { throw new Error(str + ' should contain string in between ' + prefixes + ' and ' + postfixes); } while (true) { try { start = this.indexOf(value, prefixes); } catch (e) { break; } value = value.substring(start.pos + start.sub.length); if (!value || value.length < 1) { throw new Error(str + ' should contain string in between ' + prefixes + ' and ' + postfixes); } } return value; } 

also, you can find all combinations in the bellow function

s = 'Part 1. Part 2. Part 3 then more text' def find_all_places(text,word): word_places = [] i=0 while True: word_place = text.find(word,i) i+=len(word)+word_place if i>=len(text): break if word_place<0: break word_places.append(word_place) return word_places def find_all_combination(text,start,end): start_places = find_all_places(text,start) end_places = find_all_places(text,end) combination_list = [] for start_place in start_places: for end_place in end_places: print(start_place) print(end_place) if start_place>=end_place: continue combination_list.append(text[start_place:end_place]) return combination_list find_all_combination(s,"Part","Part") 

result:

['Part 1. ', 'Part 1. Part 2. ', 'Part 2. '] 

One liners that return other string if there was no match. Edit: improved version uses next function, replace "not-found" with something else if needed:

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

My other method to do this, less optimal, uses regex 2nd time, still didn't found a shorter way:

import re res = ( ( re.search("AAA(.*?)ZZZ", "gfgfdAAA1234ZZZuijjk") or re.search("()","") ).group(1) ) 

You Might Also Like