在文本文件中查找匹配的最大区域

时间:2017-08-07 14:25:28

标签: python python-3.x

A.txt包含看起来像这样的行(或者是它的一小部分):

Green- Blue- 1
Red- Black- 3
Brown- Blue- 3
Black- Red- 1
Green- Blue- 1

基本上,最后一个字符串是1或3.假设上面的样本持续了很长时间,我需要做的是找到最大数量的连续行,最后有1个,同时保持3s的数量小于或等于某个数字(比方说2)。例如,假设A.txt完整地看起来像这样:

Green- Blue- 1
Red- Black- 3
Brown- Blue- 3
Black- Red- 3
Green- Blue- 1
Green- Purple- 1
Red- Black- 3
Brown- Blue- 3
Black- Red- 1
Blue- Blue- 3

然后脚本会将以下行写入另一个文本文件:

Green- Blue- 1
Green- Purple- 1
Red- Black- 3
Brown- Blue- 3
Black- Red- 1

我将如何编码?提前谢谢!

4 个答案:

答案 0 :(得分:3)

你真的别无选择地遍历整个文件,跟踪最大的序列。这是我的看法,用一个函数封装:它使用一个堆栈并逐行遍历文件,因此它对于大型输入文件应该是内存效率。

def foo(in_file, out_file, max_count):
    biggest, stack = [], []
    count = 0
    with open(in_file) as f:
        for line in f:
            if line[-2] == '3':
                count += 1
            if count > max_count:
                if len(stack) > len(biggest):
                    biggest = list(stack)
                # this line trims the list after the first element that ends with '3'
                stack = stack[stack.index(next(x for x in stack if x[-2] == '3')) + 1:]
                count = max_count
            stack.append(line)

    with open(out_file, 'w') as f:
        f.write(''.join(max(biggest, stack)))

注意:仅当文件末尾包含空行时,这将按预期工作,并假定max_count始终大于0(否则调用{{ 1}}抛出一个未处理的异常。)

答案 1 :(得分:1)

首先,起始字符串完全不相关。其次,可能有100种方法可以解决这个问题。我只想列出我认为最好的那个

我们还可以假设起始边界始终为:

a)清单的开头

b)在3

之后

我们还可以假设结束边界始终为:

a)清单的结尾

b)就在3

之前

所以,让我们做一个新的

threes = [-1, ... numbers.length + 1]

其中...是每个3的行号。我将-1和numbers.length + 1添加到列表中以“假装”我们的列表被两个3包围,以简化逻辑

由于未在问题陈述中指定,我们还可以假设列表将始终包含至少2个3,如果可能的话。原因是,这将给我们带来最大的范围。

现在,我们需要做的就是找到任意两个三分之一的最大行号范围。

max_range = -1 # number of lines between two 3s.
max_start = -1 # start line
max_end = -1   # end line

if len(threes) == 2: # special case here.  If the original list contains no 3s, we will take the whole list.
    max_start = threes[0]
    max_end = threes[1]
    max_range = max_end - max_start
else:
    for i in range(len(threes) - 2):
        # The general case.  Find the range between any two consecutive 3s.
        start = threes[i]
        end = threes[i + 2]
        range = end - start

        if range > max_range:
            max_start = start
            max_end = end
            max_range = range
max_start += 1
max_end -= 1
max_range -= 2

这里有一些边缘情况需要解决,但这应该可以让你开始。

第一个边缘情况(在问题中没有真正定义)如果我最终得到[1,1,1,3,3]会发生什么?我应该拿0-3,0-4还是0-5?一切似乎都是有效的解决方案。在这段代码中,我取0-5因为没有指定,它使代码更简单。

答案 2 :(得分:1)

您可以查看使用itertools.groupby

的组合存储索引的内容
txt = '''Green- Blue- 1
Red- Black- 3
Brown- Blue- 3
Black- Red- 3
Green- Blue- 1
Green- Purple- 1
Red- Black- 3
Brown- Blue- 3
Black- Red- 1
Blue- Blue- 3'''

import operator
from itertools import groupby
str_lst = list( enumerate( txt.split('\n') ) )

grp_lst = [ list(g) for k, g in groupby( [ (k,v[-1]) for k, v in str_lst ], key=operator.itemgetter(1) ) ]
filter_lst  = [ (i[0], len(i)) for i in grp_list if i[0][1] == '1' ]

for i in grp_list:
    if i[0] == max( dict(filter_lst).items(), key=operator.itemgetter(1) )[0]:
        idx = grp_list.index(i)
        break

for i in sum( grp_lst[idx:idx+3], [] ):
    print (str_lst[i[0]][1])

输出:

Green- Blue- 1
Green- Purple- 1
Red- Black- 3
Brown- Blue- 3
Black- Red- 1

答案 3 :(得分:1)

这是我的解决方案。

首先,读取文件并仅提取您实际需要的数据,即最后一位数字。

x = ''
for i, line in enumerate(txt.split('\n')):
    try:
        x += line[-1]
    except IndexError:
        pass

你最终得到一个包含所有1和3的字符串,因为它们一行一行地出现。

>>>print x
'1333113313'

此时,您可以迭代此字符串并收集所有可能不包含3次超过两次的子字符串。您可以跟踪字符串第一个字母的索引及其长度。

results = {}
for i, n in enumerate(x):
    for idx in range(i+1, len(x)):
        if x[i:idx].count('3') <= 2:
            results[i] = len(x[i:idx])
        else:
            break

最后,根据长度对结果进行排序,最后得到最长序列开始的行号以及它持续的行数。

m = sorted(results.items(), key=operator.itemgetter(1))[-1]
>>>print m
(4, 5)

您可以使用此信息来编写输出文件。因此,您将从第4行开始保存5行。

with open('myfile.txt', 'r') as inp, open('out.txt', 'w') as out:
    for line in inp.readlines()[m[0]:m[0]+m[1]]
        out.write(line)