Găsește șirul de caractere între două subșiruri [duplicate] (Programare, String, Python, Substring)

John Howard a intrebat.
a intrebat.

Cum pot găsi un șir de caractere între două subșiruri ('123STRINGabc' -> 'STRING')?

Metoda mea actuală este următoarea:

>>> start = 'asdf=5;'
>>> end = '123jasd'
>>> s = 'asdf=5;iwantthis123jasd'
>>> print((s.split(start))[1].split(end)[0])
iwantthis

Cu toate acestea, acest lucru pare foarte ineficient și ne-pitic. Care este o modalitate mai bună de a face așa ceva?

Am uitat să menționez: S-ar putea ca șirul să nu înceapă și să se termine cu start și end. Ele pot avea mai multe caractere înainte și după.

Comentarii

  • Informația dvs. suplimentară face aproape necesară utilizarea regexurilor pentru o corectitudine maximă. –  > Por Jesse Dhillon.
  • 20

  • Ce este în neregulă cu propria ta soluție? De fapt, o prefer pe cea pe care ai acceptat-o. –  > Por reubano.
  • Am încercat și eu să fac acest lucru, dar pentru mai multe instanțe se pare că folosirea lui *? pentru a face o căutare fără lăcomie și apoi doar tăierea șirului cu s[s.find(end)] a funcționat pentru urmărirea mai multor instanțe –  > Por lathomas64.
20 răspunsuri
Nikolaus Gradwohl
import re

s = 'asdf=5;iwantthis123jasd'
result = re.search('asdf=5;(.*)123jasd', s)
print(result.group(1))

Comentarii

  • @Jesse Dhillon — ce zici de sugestia lui @Tim McNamara de ceva de genul ''.join(start,test,end) in a_string? –  > Por jdd.
  • Această metodă este mai scurtă și este similară cu metoda javascript. –  > Por leonneo.
  • Va funcționa dacă există spații în șirul de început și în cel de sfârșit? –  > Por chishaku.
  • Ce se întâmplă dacă am nevoie să găsesc între 2 subșiruri și al doilea se repetă după primul? Ceva de genul acesta: s= ‘asdf=5;I_WANT_ONLY_THIS123jasdNOT_THIS123jasd – –  > Por Denis Soto.
  • Adăugați ? pentru a nu fi lacomă result = re.search('asdf=5;(.*?)123jasd', s) –  > Por do-ic.
cji
s = "123123STRINGabcabc"

def find_between( s, first, last ):
    try:
        start = s.index( first ) + len( first )
        end = s.index( last, start )
        return s[start:end]
    except ValueError:
        return ""

def find_between_r( s, first, last ):
    try:
        start = s.rindex( first ) + len( first )
        end = s.rindex( last, start )
        return s[start:end]
    except ValueError:
        return ""


print find_between( s, "123", "abc" )
print find_between_r( s, "123", "abc" )

dă:

123STRING
STRINGabc

M-am gândit că ar trebui să fie remarcat – în funcție de ce comportament aveți nevoie, puteți amesteca index și rindex apeluri sau să mergeți cu una dintre versiunile de mai sus (este echivalentul regex-ului (.*) și (.*?) grupuri).

Comentarii

    40

  • A spus că a vrut o modalitate care să fie mai pythonică, iar aceasta este în mod clar mai puțin. Nu sunt sigur de ce a fost ales acest răspuns, chiar și soluția proprie a lui OP este mai bună. –  > Por Jesse Dhillon.
  • De acord. Aș folosi soluția lui @Tim McNamara , sau sugestia aceluiași de ceva de genul start+test+end in substring –  > Por jdd.
  • Corect, deci este mai puțin pythonic, ok. Este mai puțin eficientă decât regexpurile? Și mai este și răspunsul lui @Prabhu pe care trebuie să îl downvotezi, deoarece sugerează aceeași soluție. –  > Por cji.
  • +1 de asemenea, pentru o soluție mai generică și reutilizabilă (prin import). –  > Por Ida.
  • +1, deoarece funcționează mai bine decât celelalte soluții în cazul în care end este găsit de mai multe ori. Dar sunt de acord că soluția lui OP este mai simplă. –  > Por reubano.
ansetou
start = 'asdf=5;'
end = '123jasd'
s = 'asdf=5;iwantthis123jasd'
print s[s.find(start)+len(start):s.rfind(end)]

oferă

iwantthis

Comentarii

  • Am votat asta pentru că funcționează indiferent de mărimea șirului de intrare. Unele dintre celelalte metode presupun că știi din timp lungimea. –  > Por Kenny Powers.
  • da, funcționează fără dimensiunea de intrare, însă presupune că șirul există –  > Por Kevin Crum.
Tim McNamara
s[len(start):-len(end)]

Comentarii

  • Acest lucru este foarte frumos, presupunând că începutul și sfârșitul sunt întotdeauna la începutul și sfârșitul șirului. În caz contrar, aș folosi probabil un regex. –  > Por jdd.
  • Am mers pe cel mai pythonic răspuns la întrebarea inițială la care m-am putut gândi. Testarea folosind in operator ar fi probabil mai rapid decât regexp. –  > Por Tim McNamara.
Tim McNamara

Formatarea șirurilor de caractere adaugă o anumită flexibilitate la ceea ce a sugerat Nikolaus Gradwohl. start și end pot fi acum modificate după cum se dorește.

import re

s = 'asdf=5;iwantthis123jasd'
start = 'asdf=5;'
end = '123jasd'

result = re.search('%s(.*)%s' % (start, end), s).group(1)
print(result)

Comentarii

  • Primesc următorul mesaj: 'NoneType' object has no attribute 'group' –  > Por Dentrax.
  • Asta înseamnă că nu s-a găsit o potrivire. Verificați expresia regulată. –  > Por Tim McNamara.
  • @Dentrax are dreptate: nu ar trebui să returneze nimic, nu o eroare –  > Por cwhisperer.
  • Cred că Tim se referă la faptul că căutarea ar trebui să returneze None (Nimic), deoarece nu a existat nicio potrivire. Deoarece căutarea a returnat „None”, aplicarea lui .group(1) la sfârșit cauzează eroarea. –  > Por MTay.
Fernando Wittmann

Dacă nu doriți să importați nimic, încercați metoda șirului de caractere .index():

text = 'I want to find a string between two substrings'
left = 'find a '
right = 'between two'

# Output: 'string'
print(text[text.index(left)+len(left):text.index(right)])

Comentarii

  • Îmi place la nebunie. simplă, pe o singură linie, suficient de clară, fără importuri suplimentare și funcționează din start. Nu am idee care este treaba cu răspunsurile prea inginerești de mai sus. –  > Por PaulB.
  • Aceasta nu verifică dacă textul „corect” se află de fapt în partea dreaptă a textului. Dacă există orice apariție a lui „right” înaintea textului, nu va funcționa. –  > Por AndreFeijo.
  • @AndreFeijo Sunt de acord cu tine, aceasta a fost prima mea soluție atunci când am încercat să extrag texte și am vrut să evit sintaxa ciudată a regex-ului. Cu toate acestea, în situații precum cele menționate de tine, aș folosi regex în schimb. –  > Por Fernando Wittmann.
reubano

Doar convertesc soluția proprie a OP-ului într-un răspuns:

def find_between(s, start, end):
  return (s.split(start))[1].split(end)[0]

Comentarii

  • Dacă faci din soluția altcuiva ca fiind a ta, probabil că ar trebui să o transformi într-un Wiki comunitar. –  > Por David Arenburg.
John La Rooy

Iată o modalitate de a face acest lucru

_,_,rest = s.partition(start)
result,_,_ = rest.partition(end)
print result

O altă modalitate care utilizează regexp

import re
print re.findall(re.escape(start)+"(.*)"+re.escape(end),s)[0]

sau

print re.search(re.escape(start)+"(.*)"+re.escape(end),s).group(1)

tstoev
source='your token [email protected] and maybe [email protected] or maybe [email protected]'
start_sep='_'
end_sep='@df'
result=[]
tmp=source.split(start_sep)
for par in tmp:
  if end_sep in par:
    result.append(par.split(end_sep)[0])

print result

trebuie să arate:aici0, aici1, aici2

regex-ul este mai bun, dar va necesita o librărie suplimentară și poate doriți să folosiți doar python.

Comentarii

  • Acest lucru a funcționat pentru mine. Vă mulțumim pentru extinderea soluției pentru mai multe apariții. –  > Por Sterex.
  • Am fost exact în căutarea acestui lucru, Ajută pentru mai multe apariții, Acest post are nevoie de mai multe upvotes :p. –  > Por ohsoifelse.
Reinstaurați-o pe Monica – Goodbye SE

Pentru a extrage STRING, încercați:

myString = '123STRINGabc'
startString = '123'
endString = 'abc'

mySubString=myString[myString.find(startString)+len(startString):myString.find(endString)]

Mnyikka

Iată o funcție pe care am făcut-o pentru a returna o listă cu un șir (sau mai multe) între string1 și string2 căutat.

def GetListOfSubstrings(stringSubject,string1,string2):
    MyList = []
    intstart=0
    strlength=len(stringSubject)
    continueloop = 1

    while(intstart < strlength and continueloop == 1):
        intindex1=stringSubject.find(string1,intstart)
        if(intindex1 != -1): #The substring was found, lets proceed
            intindex1 = intindex1+len(string1)
            intindex2 = stringSubject.find(string2,intindex1)
            if(intindex2 != -1):
                subsequence=stringSubject[intindex1:intindex2]
                MyList.append(subsequence)
                intstart=intindex2+len(string2)
            else:
                continueloop=0
        else:
            continueloop=0
    return MyList


#Usage Example
mystring="s123y123o123pp123y6"
List = GetListOfSubstrings(mystring,"1","y68")
for x in range(0, len(List)):
               print(List[x])
output:


mystring="s123y123o123pp123y6"
List = GetListOfSubstrings(mystring,"1","3")
for x in range(0, len(List)):
              print(List[x])
output:
    2
    2
    2
    2

mystring="s123y123o123pp123y6"
List = GetListOfSubstrings(mystring,"1","y")
for x in range(0, len(List)):
               print(List[x])
output:
23
23o123pp123

Comentarii

  • Un răspuns foarte bun și util. Vă mulțumesc! –  > Por ibarant.
  • Un răspuns extraordinar. Eu aș angaja un tip ca tine –  > Por Abhishek Singh.
Wesley Kitlasten

Aceste soluții presupun că șirul de început și șirul final sunt diferite. Iată o soluție pe care o folosesc pentru un fișier întreg atunci când indicatorii inițiali și finali sunt identici, presupunând că întregul fișier este citit folosind readlines():

def extractstring(line,flag='$'):
    if flag in line: # $ is the flag
        dex1=line.index(flag)
        subline=line[dex1+1:-1] #leave out flag (+1) to end of line
        dex2=subline.index(flag)
        string=subline[0:dex2].strip() #does not include last flag, strip whitespace
    return(string)

Exemplu:

lines=['asdf 1qr3 qtqay 45q at $A NEWT?$ asdfa afeasd',
    'afafoaltat $I GOT BETTER!$ derpity derp derp']
for line in lines:
    string=extractstring(line,flag='$')
    print(string)

Dă:

A NEWT?
I GOT BETTER!

thecollinsprogram

Puteți folosi pur și simplu acest cod sau puteți copia funcția de mai jos. Toate acestea se află perfect într-o singură linie.

def substring(whole, sub1, sub2):
    return whole[whole.index(sub1) : whole.index(sub2)]

Dacă executați funcția după cum urmează.

print(substring("5+(5*2)+2", "(", "("))

Veți rămâne probabil cu rezultatul:

(5*2

în loc de

5*2

Dacă doriți să aveți sub-secvențe la sfârșitul ieșirii, codul trebuie să arate ca mai jos.

return whole[whole.index(sub1) : whole.index(sub2) + 1]

Dar dacă nu doriți ca subșirurile să fie la sfârșit, +1 trebuie să fie pe prima valoare.

return whole[whole.index(sub1) + 1 : whole.index(sub2)]

Dragoste și pace – Joe Codeswell

Acesta este, în esență, răspunsul lui cji – 30 iul ’10 la 5:58. Am schimbat structura try except pentru o mai mare claritate în ceea ce privește ceea ce a cauzat excepția.

def find_between( inputStr, firstSubstr, lastSubstr ):
'''
find between firstSubstr and lastSubstr in inputStr  STARTING FROM THE LEFT
    http://stackoverflow.com/questions/3368969/find-string-between-two-substrings
        above also has a func that does this FROM THE RIGHT   
'''
start, end = (-1,-1)
try:
    start = inputStr.index( firstSubstr ) + len( firstSubstr )
except ValueError:
    print '    ValueError: ',
    print "firstSubstr=%s  -  "%( firstSubstr ), 
    print sys.exc_info()[1]

try:
    end = inputStr.index( lastSubstr, start )       
except ValueError:
    print '    ValueError: ',
    print "lastSubstr=%s  -  "%( lastSubstr ), 
    print sys.exc_info()[1]

return inputStr[start:end]    

josh

Metoda mea va fi de a face ceva de genul,

find index of start string in s => i
find index of end string in s => j

substring = substring(i+len(start) to j-1)

Tony Veijalainen

Acest lucru am postat înainte ca fragment de cod în Daniweb:

# picking up piece of string between separators
# function using partition, like partition, but drops the separators
def between(left,right,s):
    before,_,a = s.partition(left)
    a,_,after = a.partition(right)
    return before,a,after

s = "bla bla blaa <a>data</a> lsdjfasdjöf (important notice) 'Daniweb forum' tcha tcha tchaa"
print between('<a>','</a>',s)
print between('(',')',s)
print between("'","'",s)

""" Output:
('bla bla blaa ', 'data', " lsdjfasdjxc3xb6f (important notice) 'Daniweb forum' tcha tcha tchaa")
('bla bla blaa <a>data</a> lsdjfasdjxc3xb6f ', 'important notice', " 'Daniweb forum' tcha tcha tchaa")
('bla bla blaa <a>data</a> lsdjfasdjxc3xb6f (important notice) ', 'Daniweb forum', ' tcha tcha tchaa')
"""

AXO
from timeit import timeit
from re import search, DOTALL


def partition_find(string, start, end):
    return string.partition(start)[2].rpartition(end)[0]


def re_find(string, start, end):
    # applying re.escape to start and end would be safer
    return search(start + '(.*)' + end, string, DOTALL).group(1)


def index_find(string, start, end):
    return string[string.find(start) + len(start):string.rfind(end)]


# The wikitext of "Alan Turing law" article form English Wikipeida
# https://en.wikipedia.org/w/index.php?title=Alan_Turing_law&action=edit&oldid=763725886
string = """..."""
start = '==Proposals=='
end = '==Rival bills=='

assert index_find(string, start, end) 
       == partition_find(string, start, end) 
       == re_find(string, start, end)

print('index_find', timeit(
    'index_find(string, start, end)',
    globals=globals(),
    number=100_000,
))

print('partition_find', timeit(
    'partition_find(string, start, end)',
    globals=globals(),
    number=100_000,
))

print('re_find', timeit(
    're_find(string, start, end)',
    globals=globals(),
    number=100_000,
))

Rezultat:

index_find 0.35047444528454114
partition_find 0.5327825636197754
re_find 7.552149639286381

re_find a fost de aproape 20 de ori mai lent decât index_find în acest exemplu.

Matthew Dunn

Parsarea textului cu delimitatori de pe diferite platforme de e-mail a reprezentat o versiune mai mare a acestei probleme. În general, acestea au un START și un STOP. Caracterele de delimitare pentru wildcards continuau să blocheze regex-ul. Problema cu divizarea este menționată aici & în altă parte – oops, caracterul de delimitare a dispărut. M-am gândit să folosesc replace() pentru a da split() altceva de consumat. O bucată de cod:

nuke = '~~~'
start = '|*'
stop = '*|'
julien = (textIn.replace(start,nuke + start).replace(stop,stop + nuke).split(nuke))
keep = [chunk for chunk in julien if start in chunk and stop in chunk]
logging.info('keep: %s',keep)

Akshay

În urma răspunsului lui Nikolaus Gradwohl, am avut nevoie să obțin numărul de versiune (i.e., 0.0.2) între(‘ui:’ și ‘-‘) din conținutul fișierului de mai jos (nume fișier: docker-compose.yml):

    version: '3.1'
services:
  ui:
    image: repo-pkg.dev.io:21/website/ui:0.0.2-QA1
    #network_mode: host
    ports:
      - 443:9999
    ulimits:
      nofile:test

și iată cum a funcționat pentru mine (script python):

import re, sys

f = open('docker-compose.yml', 'r')
lines = f.read()
result = re.search('ui:(.*)-', lines)
print result.group(1)


Result:
0.0.2

Chris Martin

Acest lucru mi se pare mult mai direct:

import re

s = 'asdf=5;iwantthis123jasd'
x= re.search('iwantthis',s)
print(s[x.start():x.end()])

Comentarii

  • Acest lucru necesită să cunoașteți șirul pe care îl căutați, nu găsește orice șir care se află între cele două subșiruri, așa cum a solicitat OP. OP vrea să poată obține mijlocul, indiferent care este acesta, iar acest răspuns ar necesita să știți mijlocul înainte de a începe. –  > Por Korzak.