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


335

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

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

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

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

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

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

Ответы:


588

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

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
Второе решение лучше, если шаблон соответствует большей части времени, потому что его проще попросить прощения, чем разрешения. ,
Бенгт

7
Разве индексирование не начинается с 0? Таким образом, вам нужно будет использовать группу (0) вместо группы (1)?
Александр

22
@Alexander, no, group (0) вернет полную совпавшую строку: AAA1234ZZZ, а group (1) вернет только символы, соответствующие первой группе: 1234
Юрий К

1
@Bengt: Почему это? Первое решение выглядит довольно простым для меня, и в нем меньше строк кода.
Здравствуйте, до свидания,

5
В этом выражении? изменяет +, чтобы быть не жадным, т.е. он будет соответствовать любое количество раз от 1 и выше, но как можно меньше, только расширяясь по мере необходимости. без? первая группа будет соответствовать gfgfAAA2ZZZkeAAA43ZZZonife как 2ZZZkeAAA43, но с? он будет соответствовать только 2, тогда поиск по множеству (или его удаление и повторный поиск) будет соответствовать 43.
Dom

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

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


9
Кажется, вопрос подразумевает, что входной текст всегда будет содержать как «AAA», так и «ZZZ». Если это не так, ваш ответ ужасно терпит неудачу (под этим я подразумеваю, что он возвращает что-то совершенно неправильное вместо пустой строки или выдачи исключения; подумайте «привет там» как входная строка).
tzot

@ user225312 reСпособ не быстрее, хотя?
confused00

1
Проголосуйте, но я бы использовал "x = 'AAA'; s.find (x) + len (x)" вместо "s.find ('AAA') + 3" для удобства обслуживания.
Алекс

1
Если какой-либо из токенов не может быть найден в s, s.findвернется -1. оператор среза s[begin:end] примет его как допустимый индекс и вернет нежелательную подстроку.
Рибамар

@ confused00 найти намного быстрее, чем re stackoverflow.com/questions/4901523/…
Клаудиу Крянгэ

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


6
Этот ответ, вероятно, заслуживает большего количества голосов. Строковый метод - самый надежный способ. Это не нуждается в попытке / кроме.
ChaimG

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

Отлично, большое спасибо! - это работает для строк и не требует регулярных выражений
Alex


12

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

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

@ user1810100 упомянул по существу, что почти ровно за 5 лет до того, как вы это опубликовали ...
Джон

10

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

>>> import re

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

>>> ['1234']

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


8

Вы можете использовать модуль 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]))



2

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

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

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

Кроме того, в некоторых случаях вы можете увидеть символы 'r' перед определением регулярного выражения. Если префикса r нет, вам нужно использовать escape-символы, как в 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) )
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.