Как извлечь подстроку между двумя маркерами?

Допустим, у меня есть строка 'gfgfdAAA1234ZZZuijjk' и я хочу извлечь только '1234' часть.

Я только знаю, что будет несколько персонажей прямо перед AAA, и после ZZZ часть меня интересует 1234,

С sed можно сделать что-то подобное со строкой:

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

И это даст мне 1234 в следствии.

Как сделать то же самое в Python?

23 ответа

Решение

Использование регулярных выражений - документация для дальнейшего использования

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

Затем вы можете использовать регулярные выражения с модулем re, если хотите, но это не обязательно в вашем случае.

регулярное выражение

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 Challenge?

Удивило, что никто не упомянул об этом, что является моей быстрой версией для одноразовых скриптов:

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

Вы можете сделать, используя только одну строку кода

>>> import re

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

>>> ['1234']

результат получит список...

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

В Python извлечение строки формы подстроки может быть сделано с помощью findall метод в регулярном выражении (re) модуль.

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

Вы можете использовать модуль re для этого:

>>> import re
>>> re.compile(".*AAA(.*)ZZZ.*").match("gfgfdAAA1234ZZZuijjk").groups()
('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)])

дает

string

С помощью sed можно сделать что-то подобное со строкой:

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

И это даст мне 1234 в результате.

Вы могли бы сделать то же самое с re.sub функция с использованием того же регулярного выражения.

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

В базовом седе группа захвата представлена \(..\), но в питоне это было представлено (..),

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

Использование 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']]

один лайнер с Python 3.8

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

Вы можете найти первую подстроку с этой функцией в вашем коде (по индексу символов). Также вы можете найти то, что находится после подстроки.

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

На всякий случай кому-то придется делать то же самое, что и я. Я должен был извлечь все в скобках в строке. Например, если у меня есть такая строка, как "Президент США (Барак Обама) встретился с…", и я хочу получить только "Барак Обама", это решение:

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

Т.е. вам нужно заблокировать скобки с slash \ знак. Хотя это проблема более регулярных выражений, чем у Python.

Кроме того, в некоторых случаях вы можете увидеть символы "r" перед определением регулярного выражения. Если префикса r нет, вам нужно использовать escape-символы, как в C. Здесь более подробно об этом.

Если вы хотите найти несколько вхождений.

      content ="Prefix_helloworld_Suffix_stuff_Prefix_42_Suffix_andsoon"
strings = []
for c in content.split('Prefix_'):
    spos = c.find('_Suffix')
    if spos!=-1:
        strings.append( c[:spos])
print( strings )

Или быстрее:

      strings = [ c[:c.find('_Suffix')] for c in content.split('Prefix_') if c.find('_Suffix')!=-1 ]

Кроме того, вы можете найти все комбинации в функции ниже

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

результат:

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

простой подход может быть следующим:

      string_to_search_in = 'could be anything'
start = string_to_search_in.find(str("sub string u want to identify"))
length = len("sub string u want to identify")
First_part_removed = string_to_search_in[start:]
end_coord = length
Extracted_substring=First_part_removed[:end_coord]

Машинопись. Получает строку между двумя другими строками.

Ищет самую короткую строку между префиксами и постфиксами

префиксы - строка / массив строк / null (означает поиск с начала).

postfixes - строка / массив строк / null (означает поиск до конца).

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;
}

Другой способ сделать это - использовать списки (предположим, что искомая подстрока состоит только из чисел):

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

Вот решение без регулярных выражений, которое также учитывает сценарии, в которых первая подстрока содержит вторую подстроку. Эта функция найдет подстроку, только если второй маркер находится после первого маркера.

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

Если вы хотите проверить, существуют ли подстроки, и вернуть пустую строку, если их нет:

      def substr_between(str_all, first_string, last_string):
    pos1 = str_all.find(first_string)
    if pos1 < 0:
        return ""
    pos1 += len(first_string)
    pos2 = str_all[pos1:].find(last_string)
    if pos2 < 0:
        return ""
    return str_all[pos1:pos1 + pos2]

Один лайнер, который возвращает другую строку, если не было совпадения. Редактировать: улучшенная версия использует 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) )
Другие вопросы по тегам