Come estrarre la sottostringa tra due marcatori?


335

Diciamo che ho una stringa 'gfgfdAAA1234ZZZuijjk'e voglio estrarre solo la '1234'parte.

So solo quali saranno i pochi personaggi direttamente prima AAAe dopo ZZZla parte che mi interessa 1234.

Con sedè possibile fare qualcosa del genere con una stringa:

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

E questo mi darà di 1234conseguenza.

Come fare la stessa cosa in Python?

Risposte:


588

Utilizzo di espressioni regolari: documentazione per ulteriori riferimenti

import re

text = 'gfgfdAAA1234ZZZuijjk'

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

# found: 1234

o:

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
La seconda soluzione è migliore, se lo schema corrisponde per la maggior parte del tempo, perché è più facile chiedere perdono che permesso. .
Bengt,

7
L'indicizzazione non inizia da 0? Quindi dovresti usare il gruppo (0) invece del gruppo (1)?
Alexander

22
@Alexander, no, il gruppo (0) restituirà la stringa con corrispondenza completa: AAA1234ZZZ, e il gruppo (1) restituirà solo i caratteri corrispondenti al primo gruppo: 1234
Yurii K

1
@Bengt: perché? La prima soluzione mi sembra abbastanza semplice e ha meno righe di codice.
Ciao Arrivederci

5
In questa espressione il? modifica il + in modo che non sia avido, ovvero. corrisponderà un numero qualsiasi di volte da 1 in su ma il meno possibile, espandendosi solo se necessario. senza il?, il primo gruppo corrisponderebbe a gfgfAAA2ZZZkeAAA43ZZZonife come 2ZZZkeAAA43, ma con il? corrisponderebbe solo al 2, quindi la ricerca di multipli (o la sua eliminazione e la ricerca di nuovo) corrisponderebbe al 43.
Dom

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

Quindi puoi usare regexps anche con il modulo re, se vuoi, ma non è necessario nel tuo caso.


9
La domanda sembra implicare che il testo di input conterrà sempre sia "AAA" che "ZZZ". In caso contrario, la tua risposta fallisce in modo orribile (con questo intendo che restituisce qualcosa di completamente sbagliato invece di una stringa vuota o che genera un'eccezione; pensa "ciao lì" come stringa di input).
martedì

@ user225312 Tuttavia il remetodo non è più veloce?
confused00

1
Voteup, ma vorrei usare "x = 'AAA'; s.find (x) + len (x)" invece di "s.find ('AAA') + 3" per manutenibilità.
Alex,

1
Se uno qualsiasi dei token non può essere trovato nel s, s.findritornerà -1. l'operatore slicing s[begin:end] lo accetterà come indice valido e restituirà una sottostringa indesiderata.
ribamar,

@ confused00 find è molto più veloce di re stackoverflow.com/questions/4901523/…
Claudiu Creanga,

65

espressione regolare

import re

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

Il precedente esempio non funzionerà con un AttributeErrorse non ci sono "AAA" e "ZZZ" inyour_text

metodi di stringa

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

Quanto sopra restituirà una stringa vuota se "AAA" o "ZZZ" non esistono your_text.

PS Python Challenge?


6
Questa risposta probabilmente merita più voti positivi. Il metodo stringa è il modo più affidabile. Non ha bisogno di un tentativo / tranne.
ChaimG

... carino, anche se limitato. la partizione non è basata su regex, quindi funziona solo in questa istanza perché la stringa di ricerca era limitata da valori letterali fissi
GreenAsJade

Grazie mille! - Funziona con le stringhe e non richiede regex
Alex

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

1
AttributeError: 'NoneType' object has no attribute 'groups'- se non ci sono AAA, ZZZ nella stringa ...
eumiro,

12

Sorpreso che nessuno abbia menzionato questo, che è la mia versione rapida per gli script una tantum:

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

@ user1810100 menzionato essenzialmente che quasi esattamente 5 anni prima del tuo post ...
John

10

puoi farlo usando solo una riga di codice

>>> import re

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

>>> ['1234']

il risultato riceverà l'elenco ...


8

È possibile utilizzare re module per questo:

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

5

Con sed è possibile fare qualcosa del genere con una stringa:

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

E questo mi darà 1234 di conseguenza.

Puoi fare lo stesso con la re.subfunzione usando lo stesso regex.

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

Nella sed di base, i gruppi di cattura sono rappresentati da \(..\), ma in pitone era rappresentato da (..).


5

In Python, l'estrazione della stringa del modulo di sottostringa può essere eseguita usando il findallmetodo nel remodulo espressione regolare ( ).

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

4

Puoi trovare la prima sottostringa con questa funzione nel tuo codice (per indice dei caratteri). Inoltre, puoi trovare ciò che è dopo una sottostringa.

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

Nel caso in cui qualcuno dovrà fare la stessa cosa che ho fatto io. Ho dovuto estrarre tutto tra parentesi in una riga. Ad esempio, se ho una linea come "Il presidente degli Stati Uniti (Barack Obama) ha incontrato ..." e voglio ottenere solo "Barack Obama", questa è la soluzione:

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

Vale a dire è necessario bloccare la parentesi con il slash \segno. Anche se è un problema di espressioni più regolari di Python.

Inoltre, in alcuni casi potresti vedere i simboli "r" prima della definizione di regex. Se non esiste un prefisso r, è necessario utilizzare caratteri di escape come in C. Ecco altre discussioni su questo.


2

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

che produce:

[['1234']]


0

Ecco una soluzione senza regex che tiene conto anche degli scenari in cui la prima sottostringa contiene la seconda sottostringa. Questa funzione troverà una sottostringa solo se il secondo marker è dopo il primo 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)]

0

Un altro modo per farlo è usare gli elenchi (supponendo che la sottostringa che stai cercando sia fatta solo di numeri):

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

Un liner che restituisce altra stringa se non c'era corrispondenza. Modifica: la versione migliorata utilizza la nextfunzione, sostituirla "not-found"con qualcos'altro se necessario:

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

Il mio altro metodo per farlo, meno ottimale, usa regex la seconda volta, ma non ho ancora trovato un modo più breve:

import re
res = ( ( re.search("AAA(.*?)ZZZ", "gfgfdAAA1234ZZZuijjk") or re.search("()","") ).group(1) )
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.