【问题标题】:Python index more than oncePython索引不止一次
【发布时间】:2011-03-23 18:42:23
【问题描述】:

我知道.index() 将返回子字符串在 python 中的位置。 但是,我想要的是第 n 次找到子字符串的位置,这将像这样工作:

>> s = 'abcdefacbdea'
>> s.index('a')
0
>> s.nindex('a', 1)
6
>>s.nindex('a', 2)
11

有没有办法在 python 中做到这一点?

【问题讨论】:

  • 请注意,为了符合 Python 的 0 索引性质,我可能会分别将第二次出现和第三次出现称为“1”和“2”。

标签: python string indexing substring


【解决方案1】:

怎么样...

def nindex(mystr, substr, n=0, index=0):
    for _ in xrange(n+1):
        index = mystr.index(substr, index) + 1
    return index - 1

Obs:正如str.index() 所做的那样,nindex() 在未找到 substr 时引发 ValueError

【讨论】:

    【解决方案2】:
    def nindex(needle, haystack, index=1):
         parts = haystack.split(needle)
         position = 0
         length = len(needle)
         for i in range(index - 1):
             position += len(parts[i]) + length
         return position
    

    我有兴趣看看其他解决方案,我觉得这不是特别pythonic。

    【讨论】:

      【解决方案3】:

      我可能会使用

      [index for index, value in enumerate(s) if s == 'a'][n]
      

      from itertools import islice
      next(islice((index for index, value in enumerate(s) if s == 'a'), n, None))
      

      或完全避免处理索引。

      【讨论】:

      • 我也先写了这个,但是这些东西只适用于一个字符搜索词。但是索引可以做到'asdf'.index('sd')
      • 感谢您指出这一点。这符合示例目的,但显然不太通用。如果 OP 确实需要该功能,这种技术很容易适应。
      【解决方案4】:

      这是一个 memoized 版本,它尽可能避免浪费工作,同时保持与您的规格接近 [1] 的东西(而不是做一些更理智的事情,例如循环 所有命中;-)...:

      [1]: 只是 close -- 当然不能在字符串中使用新的.nindex 方法!-)

      def nindex(haystack, needle, nrep=1, _memo={}):
        if nrep < 1:
          raise ValueError('%r < 1' % (nrep,))
        k = needle, haystack
        if k in _memo:
          where = _memo[k]
        else:
          where = _memo[k] = [-1]
        while len(where) <= nrep:
          if where[-1] is None:
            return -1
          w = haystack.find(needle, where[-1] + 1)
          if w < 0:
            where.append(None)
            return -1
          where.append(w)
        return where[nrep]
      
      s = 'abcdefacbdea'
      print nindex(s, 'a')
      print nindex(s, 'a', 2)
      print nindex(s, 'a', 3)
      

      按要求打印 0,然后是 6,然后是 11。

      【讨论】:

      • 为什么将 None 添加到 where 但返回 -1 ?这会使第一个调用结果与后续调用结果不同...
      【解决方案5】:
      >>> from re import finditer, escape
      >>> from itertools import count, izip
      
      >>> def nfind(s1, s2, n=1):
      ...    """return the index of the nth nonoverlapping occurance of s2 in s1"""
      ...    return next(j.start() for i,j in izip(count(1), finditer(escape(s2),s1)) if i==n)
      ...
      >>> nfind(s,'a')
      0
      >>> nfind(s,'a',2)
      6
      >>> nfind(s,'a',3)
      11
      

      【讨论】:

        【解决方案6】:

        是的。使用s.index('yourstring', start)写一个循环

        发现一个大胖子-1后更新……我不是写了一些代码吗???

        这是我的赎回尝试,如果需要,它允许不重叠,并在所示范围内进行测试:

        >>> def nindex(haystack, needle, n, overlapping=True):
        ...    delta = 1 if overlapping else max(1, len(needle))
        ...    start = -delta
        ...    for _unused in xrange(n):
        ...       start = haystack.index(needle, start+delta)
        ...    return start
        ...
        >>> for n in xrange(1, 11):
        ...    print n, nindex('abcdefacbdea', 'a', n)
        ...
        1 0
        2 6
        3 11
        4
        Traceback (most recent call last):
          File "<stdin>", line 2, in <module>
          File "<stdin>", line 5, in nindex
        ValueError: substring not found
        >>> for olap in (True, False):
        ...    for n in (1, 2):
        ...       print str(olap)[0], n, nindex('abababab', 'abab', n, olap)
        ...
        T 1 0
        T 2 2
        F 1 0
        F 2 4
        >>> for n in xrange(1, 8):
        ...    print n, nindex('abcde', '', n)
        ...
        1 0
        2 1
        3 2
        4 3
        5 4
        6 5
        7
        Traceback (most recent call last):
          File "<stdin>", line 2, in <module>
          File "<stdin>", line 5, in nindex
        ValueError: substring not found
        >>>
        

        【讨论】:

          【解决方案7】:
          def nindex(str, substr, index):
            slice = str
            n = 0
            while index:
              n += slice.index(substr) + len(substr)
              slice = str[n:]
              index -= 1
            return slice.index(substr) + n
          

          【讨论】:

            【解决方案8】:
            import re
            
            def nindex(text, n=1, default=-1):
                return next(
                    itertools.islice((m.start() for m in re.finditer('a', text)), n - 1, None),
                    default
                )
            
            print nindex(s)
            print nindex(s, 1)
            print nindex(s, 2)
            print nindex(s, 3)
            print nindex(s, 4)
            

            【讨论】:

              【解决方案9】:
              def ifind( s, word, start=0 ):
                  pos = s.find(word,start)
                  while -1 < pos:
                      yield pos
                      pos = s.find(word,pos+1)
              
              print list(ifind('abcdefacbdea', 'a'))     # [0, 6, 11]
              print list(ifind('eee', 'a'))              # []
              

              【讨论】:

              • 未找到 -> 开始 == -1 -> 开始 += 1 -> 开始 == 0,不停止
              • @Tony Veijalainen 谢谢,我修好了……我不应该在早上 6 点写答案;o
              【解决方案10】:

              怎么样...

              # index is 0-based
              def nindex(needle, haystack, index=0):
                   parts = haystack.split(needle)
                   if index >= len(parts)-1:
                       return -1
                   return sum(len(x) for x in parts[:index+1])+index*len(needle)
              

              【讨论】:

                【解决方案11】:
                import itertools
                def multis(search,text,start=0):
                    while start>-1:
                        f=text.find(search,start)
                        start=f
                        if start>-1:
                            yield f
                            start+=1
                
                # one based function for nth result only
                def nindex(text,search,n):
                    return itertools.islice(multis(search,text),n-1,n).next()
                
                text = 'abcdefacbdea'
                search = 'a'
                print("Hit %i: %i" % (3, nindex(text,search,3)))
                print ('All hits: %s' % list(multis(search,text)))
                

                没有索引:

                def nthpartition(search,text,n=None):
                    ## nth partition before and after or all if not n
                    if not n:
                        n=len(text) # bigger always than maximum number of n
                    for i in range(n):
                        before,search,text = text.partition(search)
                        if not search:
                            return
                        yield before,text
                
                text = 'abcdefacbdea'
                search = 'a'
                print("Searching %r in %r" % (search,text))
                
                for parts in nthpartition(search,text): print(parts)
                """Output:
                Searching 'a' in 'abcdefacbdea'
                ('', 'bcdefacbdea')
                ('bcdef', 'cbdea')
                ('cbde', '')
                """
                

                【讨论】:

                  【解决方案12】:

                  只需重复调用'index',使用最后一次调用的结果(+1)作为起始位置:

                  def nindex(needle, haystack, n):
                  "find the nth occurrence of needle in haystack"
                    pos = -1
                    for dummy in range(n):
                      pos = haystack.index(needle, pos + 1)
                    return pos
                  

                  注意:我没有测试过。

                  【讨论】:

                    【解决方案13】:

                    这个在正则表达式中完成工作。如果您修改它以缓存已编译的正则表达式(或记忆它),它不会(测试后)可能更快。

                    import re
                    
                    def nindex(s, substr, n = 1):
                        """Find the nth occurrence of substr in s."""
                        safe_substr = re.escape(substr) 
                        regex_str = ".*?(?:%s.*?){%i}(%s).*?" % (safe_substr, n - 1, safe_substr)
                        regex = re.compile(regex_str)
                        match = regex.search(s)    
                        if match is None:
                            index = None
                        else:
                            index = match.start(1)        
                        return index
                    
                    
                    # The rest of this code is just test cases...
                    for search_str in ("a", "bc"):
                        print "Looking for %s" % search_str
                        for test_str in ('abcdefacbdea',
                                         'abcdefacbdeaxxx',
                                         'xxxabcdefacbdeaxxx'):
                            for i in (0, 1, 2, 3, 4):      
                                print("%s %i index: %s" % 
                                      (test_str, i, nindex(test_str, search_str, i)))
                        print 
                    

                    输出是:

                    Looking for a
                    abcdefacbdea 0 index: None
                    abcdefacbdea 1 index: 0
                    abcdefacbdea 2 index: 6
                    abcdefacbdea 3 index: 11
                    abcdefacbdea 4 index: None
                    abcdefacbdeaxxx 0 index: None
                    abcdefacbdeaxxx 1 index: 0
                    abcdefacbdeaxxx 2 index: 6
                    abcdefacbdeaxxx 3 index: 11
                    abcdefacbdeaxxx 4 index: None
                    xxxabcdefacbdeaxxx 0 index: None
                    xxxabcdefacbdeaxxx 1 index: 3
                    xxxabcdefacbdeaxxx 2 index: 9
                    xxxabcdefacbdeaxxx 3 index: 14
                    xxxabcdefacbdeaxxx 4 index: None
                    
                    Looking for bc
                    abcdefacbdea 0 index: None
                    abcdefacbdea 1 index: 1
                    abcdefacbdea 2 index: None
                    abcdefacbdea 3 index: None
                    abcdefacbdea 4 index: None
                    abcdefacbdeaxxx 0 index: None
                    abcdefacbdeaxxx 1 index: 1
                    abcdefacbdeaxxx 2 index: None
                    abcdefacbdeaxxx 3 index: None
                    abcdefacbdeaxxx 4 index: None
                    xxxabcdefacbdeaxxx 0 index: None
                    xxxabcdefacbdeaxxx 1 index: 4
                    xxxabcdefacbdeaxxx 2 index: None
                    xxxabcdefacbdeaxxx 3 index: None
                    xxxabcdefacbdeaxxx 4 index: None
                    

                    这是记忆的版本:

                    def memoized_hedgehog_nindex(s, substr, n = 1, _memoized_regexes = {}):
                        safe_substr = re.escape(substr) 
                        regex_str = ".*?(?:%s.*?){%i}(%s).*?" % (safe_substr, n - 1, safe_substr)
                    
                        # memoize
                        key = (n, safe_substr)
                        if key in _memoized_regexes:
                            regex = _memoized_regexes[key]
                        else:
                            regex = re.compile(regex_str)
                            _memoized_regexes[key] = regex
                    
                        match = regex.search(s)    
                        if match is None:
                            index = None
                        else:
                            index = match.start(1)        
                        return index
                    

                    【讨论】:

                    • 正则表达式因换行符和其他内容而变得复杂。建议您在采用它之前再扔几块石头。
                    • 最好记住答案而不是我怀疑的正则表达式。
                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2020-03-12
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2013-06-30
                    相关资源
                    最近更新 更多