【问题标题】:Split text file after specific line in python在python中的特定行之后拆分文本文件
【发布时间】:2020-02-17 20:24:51
【问题描述】:

我正在尝试编写代码来读取 Fresco 文件并绘制结果。 Fresco 生成一个看起来像这样的大文件

theta  sigma
1        0.1
2        0.1
3        0.2
...
END
some text...
theta   sigma
1        0.3
2        0.2
...
END
more data...

我想在每个“END”之后生成一个新文件来分别分析数据。我尝试了一些针对其他答案的解决方案,例如

with open('fort.16', 'r') as infile, open('output_fort.16', 'w') as outfile:
copy= False
for line in infile:
    if line.strip() == '# legend':
        copy = True
        continue
    elif line.strip()=='End':
        copy = False
    elif copy:
        outfile.write(line)

但这不是我需要的。 我对 python 相当陌生,因此非常感谢任何帮助。

【问题讨论】:

  • 你试过infile.read().split("END")吗?
  • @Filip 如果文件很大,这将不起作用
  • 我们如何区分some text... 行和theta sigma 标题行?我们是否知道标题在整个部分中始终是相同的theta sigma,还是可以(任意)更改?是否保证some text... 行不包含数字? (顺便说一句,用# 为某些文本行添加前缀会很有用,以使它们成为cmets,这将被忽略。)
  • 但这不是我需要的。 - 为什么?它有什么问题?
  • "这不是我需要的" 你的代码和你想要的有什么不同?

标签: python


【解决方案1】:

我设法用嵌套生成器解决了这个问题:

import re

SECTION_START = re.compile(r'^\s*theta\s+sigma\s*$')
SECTION_END = re.compile(r'^\s*END\s*$')

def fresco_iter(stream):
    def inner(stream):
        # Yields each line until an end marker is found (or EOF)
        for line in stream:
            if line and not SECTION_END.match(line):
                yield line
                continue
            break

    # Find a start marker, then break off into a nested iterator
    for line in stream:
        if line:
            if SECTION_START.match(line):
                yield inner(stream)
            continue
        break

fresco_iter 方法返回一个可以进行 for 循环的生成器。它为theta sigma 对的每个部分返回 1 个生成器。

>>> with open('fort.16', 'r') as fh:
...     print(list(fresco_iter(fh)))
[<generator object fresco_iter.<locals>.inner at 0x7fbc6da15678>,
 <generator object fresco_iter.<locals>.inner at 0x7fbc6da15570>]

因此,要利用这一点,您可以创建自己的嵌套循环来处理嵌套生成器。

filename = 'fort.16'

with open(filename, 'r') as fh:
    for nested_iter in fresco_iter(fh):
        print('--- start')
        for line in nested_iter:
            print(line.rstrip())
        print('--- end')

会输出...

--- start
1        0.1
2        0.1
3        0.2
--- end
--- start
1        0.3
2        0.2
--- end

这种策略一次只能在内存中保存 1 行输入文件,因此适用于任何大小的文件,即使是在最小的设备上...因为生成器很棒。

因此,一路走来......将输出分成单独的文件:

with open(filename, 'r') as fh_in:
    for (i, nested_iter) in enumerate(fresco_iter(fh_in)):
        with open('{}.part-{:04d}'.format(filename, i), 'w') as fh_out:
            for line in nested_iter:
                fh_out.write(line)

输出数字以分隔名为 fort.16.part-0000fort.16.part-0001 的文件。

我希望这会有所帮助,祝你编码愉快!

【讨论】:

    【解决方案2】:
    fp = open("random.txt")
    
    data = []
    temp = []
    
    for i, line in enumerate(fp):
        if line.strip() == "END":
            new_file = open("file"+str(i)+".txt", "a+")
            for i in temp:
                new_file.write(i+"\n")
            temp = []
            new_file.close()
            continue
        temp.append(line.strip())
    
    fp.close()
    print(data)
    

    给你这个,每次都创建一个新文件。文件名是文件和找到“END”行的索引。 :)

    【讨论】: