如何根据正则表达式模式将文本文件拆分成更小的文件?

How to split a text file into smaller files based on regex pattern?

我有如下文件:

SCN DD1251       
            UPSTREAM               DOWNSTREAM               FILTER              
          NODE     LINK          NODE    LINK                LINK               
        DD1271      C           DD1271    R                                     
        DD1351      D           DD1351    B                                     
                    E                                                           
                                                                                
SCN DD1271       
            UPSTREAM               DOWNSTREAM               FILTER              
          NODE     LINK          NODE    LINK                LINK               
        DD1301      T           DD1301    A                                     
        DD1251      R           DD1251    C                                     
                                                                                
SCN DD1301       
            UPSTREAM               DOWNSTREAM               FILTER              
          NODE     LINK          NODE    LINK                LINK               
        DD1271      A           DD1271    T                                     
                    B                                                           
                    C                                                           
                    D                                                           
                                                                                
SCN DD1351       
            UPSTREAM               DOWNSTREAM               FILTER              
          NODE     LINK          NODE    LINK                LINK               
                    A           DD1251    D                                     
        DD1251      B                                                           
                    C                                                           
                                                                                
SCN DD1451       
            UPSTREAM               DOWNSTREAM               FILTER              
          NODE     LINK          NODE    LINK                LINK               
                    A                                                           
                    B                                                           
                    C                                                           
                                                                                
SCN DD1601       
            UPSTREAM               DOWNSTREAM               FILTER              
          NODE     LINK          NODE    LINK                LINK               
                    A                                                           
                    B                                                           
                    C                                                           
                    D                                                           
                                                                                
SCN GA0101       
            UPSTREAM               DOWNSTREAM               FILTER              
          NODE     LINK          NODE    LINK                LINK               
                    B           GC4251    D                                     
        GC420A      C           GA127A    S                                     
        GA127A      T                                                           
                                                                                
SCN GA0151       
            UPSTREAM               DOWNSTREAM               FILTER              
          NODE     LINK          NODE    LINK                LINK               
                    C           GA0401    R                   G                 
        GA0201      D           GC0051    E                   H                 
        GA0401      B           GA0201    W                                     
        GC0051      A                                                           

其中每条记录之间的间隙有一个换行符后跟 81 个空格。

我使用 regex101.com 创建了以下正则表达式,它似乎与每条记录之间的间隙相匹配:

\s{81}\n

结合下面的短循环打开文件,然后将每个部分写入一个新文件:

delimiter_pattern = re.compile(r"\s{81}\n")

with open("Junctions.txt", "r") as f:
    i = 1
    for line in f:
        if delimiter_pattern.match(line) == False:
            output = open('%d.txt' % i,'w')
            output.write(line)
        else:
            i+=1

但是,不是输出,而是像下面预期的那样说 2.txt:

SCN DD1271
            UPSTREAM               DOWNSTREAM               FILTER
          NODE     LINK          NODE    LINK                LINK
        DD1301      T           DD1301    A
        DD1251      R           DD1251    C

相反,它似乎 return 什么都没有。我试过像这样修改代码:

with open("Clean-Junction-Links1.txt", "r") as f:
    i = 1
    output = open('%d.txt' % i,'w')
    for line in f:
        if delimiter_pattern.match(line) == False:
            output.write(line)
        else:
            i+=1

但这反而是 return 数百个空白文本文件。

我的代码有什么问题,我该如何修改它才能使其正常工作?如果做不到这一点,是否有更简单的方法在不使用正则表达式的情况下在空行上拆分文件?

\s 捕获空格和换行符,所以它是 80 个空格加一个换行符得到 {81}。 逐行迭代 for line in f 时,您无法获得第二个换行符 ,除非您放入额外的逻辑来说明这一点。另外,match() returns None,不是假的。

#! /usr/bin/env python3
import re

delimiter_pattern = re .compile( r'\s{81}' )

with open( 'Junctions.txt', 'r' ) as f:
    i = 1
    for line in f:
        if delimiter_pattern .match( line ) == None:
            output = open( f'{i}.txt', 'a+' )
            output .write( line )
        else:
            i += 1

您得到空白输出是因为您正在检查一行是否与一堆空格 (\s{81}\n) 匹配,如果匹配,则您只写入该(空白)行。您需要在读取每一行时打印它,然后在您的模式匹配时跳转到一个新文件。

此外,当您使用 for line in f 时,\n 字符被删除,因此您的正则表达式将不匹配。

import re

delimiter_pattern = re.compile(r"\s{81}")

with open("Junctions.txt", "r") as f:
    fileNum = 1
    output = open(f'{fileNum}.txt','w') # f-strings require Python 3.6 but are cleaner
    for line in f:
        if not delimiter_pattern.match(line):
            output.write(line)
        else:
            output.close()
            fileNum += 1
            output = open(f'{fileNum}.txt','w')

    # Close last file
    if not output.closed:
      output.close()

您不需要使用正则表达式来执行此操作,因为您可以使用字符串 strip() 方法轻松检测块之间的间隙。

input_file = 'Clean-Junction-Links1.txt'

with open(input_file, 'r') as file:
    i = 0
    output = None

    for line in file:
        if not line.strip():  # Blank line?
            if output:
                output.close()
            output = None
        else:
            if output is None:
                i += 1
                print(f'Creating file "{i}.txt"')
                output = open(f'{i}.txt','w')
            output.write(line)

    if output:
        output.close()

print('-fini-')

另一种更简洁、更模块化的实现方式是将处理分成两个独立的任务,这两个任务在逻辑上彼此关系不大:

  1. 正在读取文件并将每条记录的行分组在一起。
  2. 将每组行写入一个单独的文件。

第一个可以实现为 generator function,它迭代地收集并生成包含记录的行组。就是下面那个叫extract_records()

input_file = 'Clean-Junction-Links1.txt'

def extract_records(filename):
    with open(filename, 'r') as file:
        lines = []
        for line in file:
            if line.strip():  # Not blank?
                lines.append(line)
            else:
                yield lines
                lines = []
        if lines:
            yield lines

for i, record in enumerate(extract_records(input_file), start=1):
    print(f'Creating file {i}.txt')
    with open(f'{i}.txt', 'w') as output:
        output.write(''.join(record))

print('-fini-')

几件事。

  1. 正在生成单个文本文件,因为您没有在循环中打开文件进行写入,而是在循环开始之前打开一个文件。

  2. 根据您想要的输出,您不希望匹配每一行的正则表达式,而是希望继续读取文件直到获得一条记录。

我已经制定了一个可行的解决方案

with open("Junctions.txt", "r") as f:
        #read file and split on 80 spaces followed by new line
        file = f.read()
        sep = " " * 80 + "\n"
        chunks = file.split(sep)

        #for each chunk of the file write to a txt file
        i = 0
        for chunk in chunks:
            with open('%d.txt' % i, 'w') as outFile:
                outFile.write(chunk)
            i += 1

这将获取文件并通过找到一个分隔符(80 个空格后跟新行)来获取您想要的所有组的列表