6

Strings in Python have a find("somestring") method that returns the index number for "somestring" in your string.

But let's say I have a string like the following:

"$5 $7 $9 Total Cost: $35 $14"

And I want to find the index of the first occurrence of '$' that occurs after the string "Total Cost" -- I'd like to be able to tell python, search for '$', starting at the index number for "Total Cost", and return the index number (relative to the entire string) for the first occurrence of '$' that you find. The find() method would return 0, and rfind() wouldn't work either in this case.

One kind of kludgy way to do this is the following:

def findStrAfterStr(myString, searchText, afterText):

    splitString = myString.split(afterText)
    myIndex = len(splitString[0]) + len(afterText) + splitString[1].find(searchText)
    return myIndex

myString = "$5   $7    $9     Total Cost: $35   $14"
searchText = "$"
afterText = "Total Cost"

findStrAfterStr(myString, searchText, afterText)

But it seems like there should be an easier way to do this, and I assume there probably is and I just don't know what it is. Thoughts?

This would be particular useful for slicing, when I find myself doing this a lot:

myString[myString.find("startingSubstr"):myString.find("endingSubstr")]

and naturally I want the "endingSubstr" to be the one that occurs after the "startingSubstr".

CQP
  • 937
  • 2
  • 11
  • 17

3 Answers3

13

Use the optional second argument of str.find:

def findStrAfterStr(myString, searchText, afterText):
    after_index = myString.index(afterText)
    return myString.find(searchText, after_index)

Or, as pythonm suggests, you can use regexps.

I recommend a "do I really need to" approach to regexps, because it's often so hard to understand what the code does when you read it again later. Also I've found that in most cases you can do the same thing without regexp, and get code that's easier to read in the bargain. Compare:

import re

def findStrAfterStr(myString, searchText, afterText):
    pattern = "{0}.*?({1})".format(re.escape(afterText), re.escape(searchText))
    match = re.search(pattern, myString)
    return match.start(1) if match else -1
Lauritz V. Thaulow
  • 49,139
  • 12
  • 73
  • 92
  • +1 for `str.find` 2nd argument. Probably want to throw in `re.escape` in there for `re.search` (especially since the example is using `$`) – Jon Clements Nov 01 '12 at 19:30
  • 1
    @Jon Yeah, I arrived at the same conclusion. I also needed to use a non-greedy modifier to `.*`. – Lauritz V. Thaulow Nov 01 '12 at 19:32
  • I think I might use `str.index` here instead of `str.find`. They do basically the same thing except that `str.index` raises a `ValueError` with the substring isn't found (instead of returning -1 which is a valid index). Consider the corner case: `s = 'foo'; i = s.find('b'); print s.find('o',i)` which incorrectly prints 2. – mgilson Nov 01 '12 at 19:40
3
def findStrAfter(myString, searchText, afterText):
    try:
        i = myString.index(afterText)
        return min(i for i,char in enumerate(myString) if myString[i:].startswith(searchText) and i>afterText)
    except ValueError:
        print "'%s' does not exist" %afterText

OR (more efficiently):

def findStrAfter(myString, searchText, afterText):
    try:
        i = myString.index(afterText)
    except ValueError:
        print "'%s' does not exist" %afterText
        raise
    try:
        return myString[i:].index(searchText)
    except ValueError:
        print "'%s' does not exist after '%s' in myString" %(searchText, afterText)
        raise

Hope this helps

inspectorG4dget
  • 110,290
  • 27
  • 149
  • 241
1

how about this?

return string.index('Total Cost:') + string[string.index('Total Cost:'):].index('$')

OR

i = string.index('Total Cost:')
return i + string[i:].index('$')
Cameron Sparr
  • 3,925
  • 2
  • 22
  • 31