如何根据正则表达式模式将文本文件拆分成更小的文件?
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-')
另一种更简洁、更模块化的实现方式是将处理分成两个独立的任务,这两个任务在逻辑上彼此关系不大:
- 正在读取文件并将每条记录的行分组在一起。
- 将每组行写入一个单独的文件。
第一个可以实现为 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-')
几件事。
正在生成单个文本文件,因为您没有在循环中打开文件进行写入,而是在循环开始之前打开一个文件。
根据您想要的输出,您不希望匹配每一行的正则表达式,而是希望继续读取文件直到获得一条记录。
我已经制定了一个可行的解决方案
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 个空格后跟新行)来获取您想要的所有组的列表
我有如下文件:
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-')
另一种更简洁、更模块化的实现方式是将处理分成两个独立的任务,这两个任务在逻辑上彼此关系不大:
- 正在读取文件并将每条记录的行分组在一起。
- 将每组行写入一个单独的文件。
第一个可以实现为 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-')
几件事。
正在生成单个文本文件,因为您没有在循环中打开文件进行写入,而是在循环开始之前打开一个文件。
根据您想要的输出,您不希望匹配每一行的正则表达式,而是希望继续读取文件直到获得一条记录。
我已经制定了一个可行的解决方案
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 个空格后跟新行)来获取您想要的所有组的列表