Як витягнути підрядку між двома маркерами?


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, ні, група (0) поверне повну відповідність рядка: AAA1234ZZZ, а група (1) поверне лише символи, які відповідають першій групі: 1234
Юрій К

1
@Bengt: Чому це? Перше рішення для мене виглядає досить просто, і воно має менше рядків коду.
HelloGoodbye

5
У цьому виразі? модифікує +, щоб бути не жадібним, тобто. вона буде відповідати будь-якій кількості разів від 1 вгору, але якомога менше, лише розширюючись, як потрібно. без?, перша група відповідатиме gfgfAAA2ZZZkeAAA43ZZZonife як 2ZZZkeAAA43, але з? вона відповідатиме лише 2, тоді пошук декількох (або їх викреслити та знову знайти) відповідатиме 43.
Дом,

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

Тоді ви можете також використовувати regexps з модулем re, якщо ви хочете, але це не обов'язково у вашому випадку.


9
Здається, з цього питання випливає, що вхідний текст завжди буде містити як "AAA", так і "ZZZ". Якщо це не так, ваша відповідь жахливо виходить з ладу (під цим я маю на увазі, що вона повертає щось зовсім неправильне замість порожнього рядка або викидає виняток; подумайте "привіт там" як вхідний рядок).
tzot

@ user225312 Хоча reметод не швидший?
confused00

1
Voteup, але я б використовував "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/…
Claudiu 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 Challenge?


6
Ця відповідь, ймовірно, заслуговує більшої кількості голосів. Строковий метод - найбільш надійний спосіб. Для цього не потрібна спроба / за винятком.
ChaimG

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

Чудово, велике спасибі! - це працює для рядків і не потребує регулярного вираження
Алекс


12

Здивовано, що ніхто цього не згадував, що є моєю швидкою версією для разових сценаріїв:

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

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

10

ви можете зробити лише один рядок коду

>>> import re

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

>>> ['1234']

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


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 групі захоплення представлені \(..\), але в пітоні вона була представлена (..).


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 немає, вам потрібно використовувати символи втечі, як, наприклад, у 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" )

Мій інший спосіб зробити це, менш оптимальний, використовує регулярний вираз 2-го разу, все ж не знайшов коротший спосіб:

import re
res = ( ( re.search("AAA(.*?)ZZZ", "gfgfdAAA1234ZZZuijjk") or re.search("()","") ).group(1) )
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.