Como extrair a substring entre dois marcadores?


334

Digamos que eu tenho uma string 'gfgfdAAA1234ZZZuijjk'e quero extrair apenas a '1234'parte.

Só sei quais serão os poucos personagens diretamente antes AAAe depois ZZZda parte em que estou interessado 1234.

Com sedisso é possível fazer algo assim com uma string:

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

E isso vai me dar 1234como resultado.

Como fazer a mesma coisa em Python?

Respostas:


586

Usando expressões regulares - documentação para referência adicional

import re

text = 'gfgfdAAA1234ZZZuijjk'

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

# found: 1234

ou:

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
A segunda solução é melhor, se o padrão corresponder na maior parte do tempo, porque é mais fácil pedir perdão do que permissão. .
Bengt

7
A indexação não começa em 0? Então você precisaria usar o grupo (0) em vez do grupo (1)?
Alexander Alexander

22
@Alexander, não, grupo (0) retornará completa string combinada: AAA1234ZZZ e grupo (1) retornará apenas caracteres pareados por primeiro grupo: 1234
Yurii K

11
@Bengt: Por que isso? A primeira solução parece bastante simples para mim e possui menos linhas de código.
HelloGoodbye 07/07

5
Nesta expressão o? modifica o + para não ser ganancioso, ou seja. corresponderá a qualquer número de vezes de 1 em diante, mas o mínimo possível, apenas expandindo conforme necessário. sem o?, o primeiro grupo corresponderia a gfgfAAA2ZZZkeAAA43ZZZife como 2ZZZkeAAA43, mas com o? ele corresponderia apenas ao 2, em seguida, procurar por múltiplos (ou tê-lo retirado e pesquisar novamente) corresponderia ao 43.
Dom

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

Então você pode usar regexps com o módulo re também, se desejar, mas isso não é necessário no seu caso.


9
A questão parece sugerir que o texto de entrada sempre conterá "AAA" e "ZZZ". Se não for esse o caso, sua resposta falha terrivelmente (com isso quero dizer que retorna algo completamente errado, em vez de uma string vazia ou lança uma exceção; pense "olá, lá" como string de entrada).
tzot 6/02/11

@ user225312 O remétodo não é mais rápido?
confused00

11
Votar, mas eu usaria "x = 'AAA'; s.find (x) + len (x)" em vez de "s.find ('AAA') + 3" para manutenção.
21417 Alex

11
Se algum dos tokens não puder ser encontrado no s, s.findretornará -1. o operador de fatiamento s[begin:end] aceitará como índice válido e retornará substring indesejado.
Ribamar # 28/17

@ confused00 find é muito mais rápido que re stackoverflow.com/questions/4901523/…
Claudiu Creanga

63

expressão regular

import re

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

O acima como está falhará com um AttributeErrorse não houver "AAA" e "ZZZ" noyour_text

métodos de string

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

O exemplo acima retornará uma string vazia se "AAA" ou "ZZZ" não existirem your_text.

Desafio PS Python?


6
Essa resposta provavelmente merece mais votos positivos. O método string é a maneira mais robusta. Não precisa de uma tentativa / exceção.
precisa

... legal, embora limitado. partição não é regex base, por isso só funciona neste caso, porque a cadeia de pesquisa foi delimitada por literais fixos
GreenAsJade

Ótimo, muito obrigado! - Isso funciona para cordas e não requer regex
Alex

AMD! realmente, partição! Muito Obrigado!
Andrey Wal

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

11
AttributeError: 'NoneType' object has no attribute 'groups'- se não houver AAA, ZZZ na string ...
eumiro

12

Surpreso que ninguém tenha mencionado isso, que é minha versão rápida para scripts únicos:

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

@ user1810100 mencionado, essencialmente, que quase exatamente 5 anos para o dia antes que você postou isso ...
John

10

você pode fazer usando apenas uma linha de código

>>> import re

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

>>> ['1234']

resultado receberá a lista ...


7

Você pode usar o módulo re para isso:

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

5

Com o sed, é possível fazer algo assim com uma string:

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

E isso me dará 1234 como resultado.

Você poderia fazer o mesmo com a re.subfunção usando o mesmo regex.

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

No sed básico, o grupo de captura é representado por \(..\), mas em python foi representado por (..).


5

Em python, a extração da string do formulário de substring pode ser feita usando o findallmétodo no remódulo expression ( ) regular .

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

4

Você pode encontrar a primeira substring com esta função no seu código (por índice de caracteres). Além disso, você pode encontrar o que está depois de uma 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]))

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

Apenas no caso de alguém ter que fazer a mesma coisa que eu. Eu tive que extrair tudo dentro de parênteses em uma linha. Por exemplo, se eu tenho uma frase como 'Presidente dos EUA (Barack Obama) se reuniu com ...' e eu quero apenas 'Barack Obama', esta é a solução:

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

Ou seja, você precisa bloquear o parêntese com o slash \sinal. Embora seja um problema sobre expressões mais regulares que o Python.

Além disso, em alguns casos, você pode ver os símbolos 'r' antes da definição do regex. Se não houver prefixo r, você precisará usar caracteres de escape como em C. Aqui está mais discussão sobre isso.


2

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

que produz:

[['1234']]


0

Aqui está uma solução sem regex que também considera os cenários em que a primeira subseqüência contém a segunda subseqüência. Esta função encontrará apenas uma substring se o segundo marcador estiver após o primeiro marcador.

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

Outra maneira de fazer isso é usar listas (supondo que a substring que você está procurando seja feita apenas de números):

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

Forros um que retornam outra sequência se não houver correspondência. Edit: versão melhorada usa a nextfunção, substitua "not-found"por outra coisa, se necessário:

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

Meu outro método para fazer isso, menos otimizado, usa regex pela segunda vez, ainda não encontrou uma maneira mais curta:

import re
res = ( ( re.search("AAA(.*?)ZZZ", "gfgfdAAA1234ZZZuijjk") or re.search("()","") ).group(1) )
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.