web-dev-qa-db-fra.com

Trouver une chaîne entre deux sous-chaînes

Comment trouver une chaîne entre deux sous-chaînes ('123STRINGabc' -> 'STRING')?

Ma méthode actuelle est la suivante:

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

Cependant, cela semble très inefficace et non pythonique. Quelle est la meilleure façon de faire quelque chose comme ça?

Oubli de mentionner: la chaîne peut ne pas commencer et se terminer par start et end. Ils peuvent avoir plus de caractères avant et après.

193
John Howard
import re

s = 'asdf=5;iwantthis123jasd'
result = re.search('asdf=5;(.*)123jasd', s)
print(result.group(1))
242
Nikolaus Gradwohl
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" )

donne:

123STRING
STRINGabc

Je pensais que cela devrait être noté - selon le comportement dont vous avez besoin, vous pouvez mélanger les appels index et rindex ou utiliser l'une des versions ci-dessus (c'est l'équivalent de regex (.*) et (.*?) groupes).

146
cji
start = 'asdf=5;'
end = '123jasd'
s = 'asdf=5;iwantthis123jasd'
print s[s.find(start)+len(start):s.rfind(end)]

donne

iwantthis
63
ansetou
s[len(start):-len(end)]
47
Tim McNamara

Le formatage des chaînes ajoute une certaine flexibilité à ce que Nikolaus Gradwohl a suggéré. start et end peuvent maintenant être modifiés à volonté.

import re

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

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

Convertir simplement la solution du PO en réponse:

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

Voici une façon de le faire

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

Une autre façon en utilisant regexp

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

ou

print re.search(re.escape(start)+"(.*)"+re.escape(end),s).group(1)
12
John La Rooy
source='your token _here0@df and maybe _here1@df or maybe _here2@df'
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

doit montrer: here0, here1, here2

la regex est meilleure mais cela nécessitera une bibliothèque supplémentaire et vous voudrez peut-être utiliser uniquement python

11
tstoev

Si vous ne souhaitez rien importer, essayez la méthode de chaîne .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)]
8
Fernando Wittmann

Pour extraire STRING, essayez:

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

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

Voici une fonction que j'ai créée pour renvoyer une liste contenant une ou plusieurs chaînes entre chaîne1 et chaîne2.

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
3
Mnyikka

Vous pouvez simplement utiliser ce code ou copier la fonction ci-dessous. Tous parfaitement en une seule ligne.

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

Si vous exécutez la fonction comme suit.

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

Vous allez probablement rester avec la sortie:

(5*2

plutôt que

5*2

Si vous voulez avoir les sous-chaînes à la fin de la sortie, le code doit ressembler à celui ci-dessous.

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

Mais si vous ne voulez pas les sous-chaînes à la fin, le +1 doit être sur la première valeur.

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

Ma méthode sera de faire quelque chose comme,

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

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

Ces solutions supposent que la chaîne de départ et la chaîne finale sont différentes. Voici une solution que j'utilise pour un fichier entier lorsque les indicateurs initial et final sont identiques, en supposant que le fichier entier soit lu à l'aide de 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)

Exemple:

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)

Donne:

A NEWT?
I GOT BETTER!
2
Wesley Kitlasten

Ceci est essentiellement la réponse de cji - 30 juillet 10 à 5:58. J'ai changé la structure try except pour un peu plus de clarté sur la cause de l'exception.

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]    

L'analyse du texte avec des délimiteurs de différentes plates-formes de messagerie a posé une version plus grande de ce problème. Ils ont généralement un START et un STOP. Les caractères séparateurs pour les caractères génériques ont continué à étouffer les expressions rationnelles. Le problème de la scission est mentionné ici et ailleurs - oups, le caractère de délimiteur est parti. Il m'est arrivé d'utiliser replace () pour donner à split () quelque chose d'autre à consommer. Morceau de code:

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)
1
Matthew Dunn

J'ai déjà posté ceci en tant que extrait de code dans 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', " lsdjfasdj\xc3\xb6f (important notice) 'Daniweb forum' tcha tcha tchaa")
('bla bla blaa <a>data</a> lsdjfasdj\xc3\xb6f ', 'important notice', " 'Daniweb forum' tcha tcha tchaa")
('bla bla blaa <a>data</a> lsdjfasdj\xc3\xb6f (important notice) ', 'Daniweb forum', ' tcha tcha tchaa')
"""
1
Tony Veijalainen
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,
))

Résultat:

index_find 0.35047444528454114
partition_find 0.5327825636197754
re_find 7.552149639286381

re_find était presque 20 fois plus lent que index_find dans cet exemple.

1
AXO

Plus loin de la réponse de Nikolaus Gradwohl, je devais obtenir le numéro de version (c'est-à-dire .0.2) entre ('ui:' et '-') du contenu du fichier ci-dessous (nom du fichier: 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

et voici comment cela a fonctionné pour moi (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
0
Akshay