Python:使用模块或正则表达式从列表中提取列表

6

我想用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]

有什么想法可以实现它吗?

预期输出是什么? - Morse
4
@Prateek 他期望的输出是由他预期的打印语句所展示的。 - Woohoojin
什么是startSequence?它是否意味着类似于startswith? - mad_
如果您想使用正则表达式进行操作,就需要使用字符串并且不能应用于整数列表。如果您想在字符串上应用它,可以按照以下方式进行操作 https://regex101.com/r/l1sx9V/1/ - Aman Chhabra
子列表可以重叠吗? - tobias_k
6个回答

3
这里有一个更通用的解决方案,不需要列表是可切片的,因此您可以将其用于其他可迭代对象,例如生成器。
我们保持一个deque,大小与开始序列相同,直到我们遇到它。然后我们将这些值添加到列表中,并继续迭代序列。当我们这样做时,我们保持一个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]]

1
看起来我们有同样的想法,但成功实现了看起来截然不同的实现方式! - Graipher

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

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

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]]

1
如果您真的想使用正则表达式,可以将整数列表更改为字符串,并以这种方式使用正则表达式。
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_
@mad_ 谢谢。我意识到我忘记了 ?,这使得正则表达式变得非贪婪。加上这个符号后,它会按照 OP 想要的输出结果进行匹配。 - Edward Minnix
你能否请发布一下输出结果?我想知道我做错了什么。感激不尽。谢谢。 - mad_
@mad_ 更新了输出。需要将int替换为ord(在更新非贪婪符号时忘记复制该更改)。 - Edward Minnix

0

只需在列表中迭代所有索引,并分别将切片与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)):


0
请使用以下方法:
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]

提供:在列表中查找子列表的起始和结束索引


0
这里有一个O(n)的解决方案,通过跟踪匹配的开始序列和结束序列的模式来找到匹配项。
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]]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接