【问题标题】:Python: Extracting lists from list with module or regular expressionPython:使用模块或正则表达式从列表中提取列表
【发布时间】:2018-12-21 23:49:07
【问题描述】:

我正在尝试使用 Python2.7 使用开始和结束模式从一个更大的整数列表中提取列表/子列表。我想用一个函数来做,但我找不到解决这个问题的库、算法或正则表达式。

def myFunctionForSublists(data, startSequence, endSequence):
    # ... todo

data = [99, 99, 1, 2, 3, 99, 99, 99, 4, 5, 6, 99, 99, 1, 2, 3, 99, 4, 5, 6, 99]

startSequence = [1,2,3]
endSequence = [4,5,6]

sublists = myFunctionForSublists(data, startSequence, endSequence)

print sublists[0] # [1, 2, 3, 99, 99, 99, 4, 5, 6]
print sublists[1] # [1, 2, 3, 99, 4, 5, 6]

有什么想法可以实现吗?

【问题讨论】:

  • 预期输出是什么?
  • @Prateek 他的预期输出显示为他期望打印语句打印的内容
  • 什么是 startSequence?是不是类似于startswith的意思?
  • 如果要使用正则表达式,则需要使用字符串,不能应用于整数列表。如果你想在String上应用它,你可以这样做regex101.com/r/l1sx9V/1
  • 子列表可以重叠吗?

标签: python regex list search


【解决方案1】:

只需迭代列表中的所有索引并将切片分别与startSequenceendSequence 进行比较。假设子列表不应该重叠,您可以对两个循环使用相同的迭代器。

def myFunctionForSublists(data, startSequence, endSequence):
    positions = iter(range(len(data)))
    for start in positions:
        if data[start:start+len(startSequence)] == startSequence:
            for end in positions:
                if data[end:end+len(endSequence)] == endSequence:
                    yield data[start:end+len(endSequence)]
                    break

这样,start 循环将在end 循环离开的地方继续。如果它们可以重叠,则为循环使用两个单独的迭代器,即for start in range(len(data)):for end in range(start+1, len(data)):

【讨论】:

    【解决方案2】:

    使用以下方法:

    def find_sub_list(sl,l):
        sll=len(sl)
        for ind in (i for i,e in enumerate(l) if e==sl[0]):
            if l[ind:ind+sll]==sl:
                return ind,ind+sll-1
    
    find_sub_list([1,2,3], data)    
    >>>(2, 4)
    find_sub_list([4,5,6], data)    
    >>>(8, 10)
    
    data[2:10+1]
    >>>[1, 2, 3, 99, 99, 99, 4, 5, 6]
    

    sublists[1] 可以采用类似的方法

    礼貌:find-starting-and-ending-indices-of-sublist-in-list

    【讨论】:

      【解决方案3】:

      这是一个 O(n) 的解决方案,它通过跟踪 startSequence 和 endSequence 的匹配模式来找到匹配项

      def myFunctionForSublists(data, startSequence, endSequence):
          start,end = tuple(startSequence), tuple(endSequence)
          l1, l2    = len(start), len(end)
          s = -1
          result = []
          for i,v in enumerate(zip(*[data[i:] for i in range(0,l1)])):
              if v == start:
                  s = i
              if v == end and s != -1:
                  result.append(data[s:i+l2])
                  s = -1
      
          return result
      
      
      print (myFunctionForSublists(data, startSequence, endSequence))
      # [[1, 2, 3, 99, 99, 99, 4, 5, 6], [1, 2, 3, 99, 4, 5, 6]]
      

      【讨论】:

        【解决方案4】:

        这是一个 itertools 方法,它使用有限长度的collections.deque 来保持适当大小的最后一个元素的缓冲区。它假定您的子列表不重叠,并且您的开始和结束序列也不重叠。

        它适用于数据、开始、结束(甚至生成器)的任何序列。

        from collections import deque
        from itertools import islice
        
        def sublists(data, start, end):
            it = iter(data)
            start, end = deque(start), deque(end)
            while True:
                x = deque(islice(it, len(start)), len(start))
                # move forward until start is found
                while x != start:
                    x.append(next(it))
                out = list(x)
                x = deque(islice(it, len(end)), len(end))
                # move forward until end is found, storing the sublist
                while x != end:
                    out.append(x[0])
                    x.append(next(it))
                out.extend(end)
                yield out
        
        data = [99, 99, 1, 2, 3, 99, 99, 99, 4, 5, 6, 99, 99, 1, 2, 3, 99, 4, 5, 6, 99]
        
        startSequence = [1,2,3]
        endSequence = [4,5,6]
        
        print(list(sublists(data, startSequence, endSequence)))
        # [[1, 2, 3, 99, 99, 99, 4, 5, 6], [1, 2, 3, 99, 4, 5, 6]]
        

        【讨论】:

          【解决方案5】:

          这是一个更通用的解决方案,它不需要列表可切片,因此您可以在其他可迭代对象上使用它,例如生成器。

          我们保持deque 的大小与start 序列的大小相同,直到遇到它。然后我们将这些值添加到列表中,并继续迭代序列。正如我们所做的那样,我们保持一个deque 结束序列的大小,直到我们看到它,同时将元素添加到我们保留的列表中。如果我们遇到结束序列,我们会yield 该列表并将deque 设置为向上扫描下一个开始序列。

          from collections import deque
          
          def gen(l, start, stop):
              start_deque = deque(start)
              end_deque = deque(stop)
              curr_deque = deque(maxlen=len(start))
              it = iter(l)
              for c in it:
                  curr_deque.append(c)
                  if curr_deque == start_deque:
                      potential = list(curr_deque)
                      curr_deque = deque(maxlen=len(stop))
                      for c in it:
                          potential.append(c)
                          curr_deque.append(c)
                          if curr_deque == end_deque:
                              yield potential
                              curr_deque = deque(maxlen=len(start))
                              break
          
          print(list(gen([99, 99, 1, 2, 3, 99, 99, 99, 4, 5, 6, 99, 99, 1, 2, 3, 99, 4, 5, 6, 99], [1,2,3], [4,5,6])))
          
          # [[1, 2, 3, 99, 99, 99, 4, 5, 6], [1, 2, 3, 99, 4, 5, 6]]
          

          【讨论】:

          • 我们似乎有相同的想法,但设法有足够不同的外观实现!
          【解决方案6】:

          如果您真的想使用正则表达式,您可以将整数列表更改为字符串并以这种方式使用正则表达式

          import re
          
          def find_span(numbers, start, end):
              # Create strings from the start and end lists.
              start_pattern = ''.join(map(chr, start))
              end_pattern = ''.join(map(chr, end))
          
              # convert the list to search into one string.
              s = ''.join(map(chr, numbers))
          
              # Create a pattern that starts and ends with the correct sublists,
              # and match all sublists. Then convert each match back to a list of
              # integers
              # The '?' is to make the regex non-greedy
              return [
                  [ord(c) for c in match]
                  for match in re.findall(rf'{start_pattern}.*?{end_pattern}', s, re.DOTALL)
              ]
          
          >>> find_span(search, start, end)  # Using OP's sample values
          [[1, 2, 3, 99, 99, 99, 4, 5, 6], [1, 2, 3, 99, 4, 5, 6]]
          

          请注意,这并不是很有效,因为它需要在每次调用时动态构建一个正则表达式。你需要使用re.DOTALL,否则它不会匹配任何包含10(这是换行符的ascii编码)的东西。但是,如果您真的想使用正则表达式,这会起作用。

          【讨论】:

          • 先用map将int转换为str。您确定它给出了 OP 发布的预期结果吗?我正在尝试正则表达式,但无法摆脱一些重叠
          • @mad_ 谢谢。我意识到我忘记了 ? 这使得正则表达式不贪婪。它确实提供了该更改所需的输出 OP。
          • 你能把输出也贴出来吗?只是想知道我做错了什么。会很感激。谢谢
          • @mad_ 更新了输出。 int 也需要替换为 ord (更新非贪婪符号时忘记复制该更改)
          猜你喜欢
          • 1970-01-01
          • 2016-08-05
          • 2013-11-24
          • 2018-08-22
          • 2019-07-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-09-02
          相关资源
          最近更新 更多