我正在尝试解析一个显示数字数据部分的文件。目标是提取每个数据块以进行进一步解析。事实是这些部分跨越多行(以\ n结尾),可能在开头有空格而该部分的最后一行显示关键字END:
1 2 3 4
2 5 6 7
3 8 9 10
*END
1 11 12 13
2 14 15 16
3 17 18 19
*END
我想用正则表达式提取的是字符串列表:
['1 2 3 4\n 2 5 6 7\n3 8 9 10',
'1 11 12 13\n2 14 15 16\n3 17 18 19']
我做:
import re
ifile = open('file.dat', 'r')
data = ifile.read()
然后我为一行构建的正则表达式模式是:
line = r'^(\s*\d+(?:\s+\d+){3}\s*)$'
pattern = re.compile(line, re.MULTILINE)
pattern.findall(data)
然后我得到:
['1 2 3 4',
' 2 5 6 7',
'3 8 9 10',
'\n\n1 11 12 13',
'2 14 15 16',
'3 17 18 19']
第一个问题是为什么'\ n'会保留在第四个字符串中?
我提取两个部分的方法是:
section = r'(?:(' + line + ')*)\*END'
pattern = re.compile(section, re.MULTILINE)
pattern.findall(data)
但我无法让它发挥作用。也许用MULTILINE旗子我不明白的东西?
编辑:
我想在我的正则表达式中指定一行(1个整数后跟3个其他整数)的结构。 vks的解决方案有效,但我想更具体一点。我尝试了以下方法:
换行:
^\s*\d+(?:\s+\d+){4}\s*$
效果很好(http://regex101.com/r/qZ6sE3/6)
对于某个部分:
((?:^\s*\d+(?:\s+\d+){4}\s*$)*)(?=\s*\*END)
这里的想法是复制线条以形成一个多线条块,并且只有当后面跟着一条带有* END的线条时才匹配它。
http://regex101.com/r/qZ6sE3/7
然而,结果并非如此。它匹配最后一行,但也有一些空字符串......理解有什么帮助吗?
此致 弗朗索瓦。
答案 0 :(得分:1)
要回答你的问题,为什么在字符1
之前出现的换行符是因为前面有两个空行。
由于数据块是基于' * END'我们可以将它用作分隔符并将文件内容分成列表。然后我们可以遍历列表并使用正则表达式来处理数据并收集数字。
#!/usr/bin/env python3
import re
data = open('file').read().split('*END')
items = []
for x in data:
items.append(re.findall('(\d+)\s+', x))
print(items)
输出
[['1', '2', '3', '4', '2', '5', '6', '7', '3', '8', '9', '10'],
['1', '11', '12', '13', '2', '14', '15', '16', '3', '17', '18', '19'], []]
此处列表items
将每个块的内容放在一个单独的列表中。
答案 1 :(得分:0)
第一个问题是为什么'\ n'会保留在第四个字符串中?
因为它们与第一个\s*
相匹配。
一般来说,我建议逐行“解析”文件,跳过空行和正则表达式解析非空行。像
这样的东西data = []
block = []
for line in ifile.readlines():
if not line.strip():
continue
elif line.startswith("*END"):
data.append(block)
block = []
elif:
m = re.match(r"(\d+)\s*(\d+)\s*(\d+)\s*(\d+)", line)
block.append(m.groups())
答案 2 :(得分:0)
我建议您使用' * END'拆分文本,然后清理生成的部分。这应该比逐行或使用正则表达式更快。我认为它也抓住了任务的本质,即找到由' * END'分隔的东西。
raw_parts = data.split('*END')
parts = []
for part in raw_parts:
part = part.strip()
if len(part) != 0:
parts.append(part)
print(parts)
在回答您关于其他类型数据与数字块混合的可能性的评论时,让我使用正则表达式建议此解决方案。关键的想法是使用re.DOTALL
使句点匹配多行。这可能是您期望从re.MULTILINE
开始的行为。
import re
pattern = re.compile('(\d.*?)\*END', re.DOTALL)
blocks = list(map(lambda s: s.strip(), pattern.findall(data)))
print(blocks)
答案 3 :(得分:0)
([\d\s]+)(?=\*END)
试试这个。抓住捕获。参见演示。
http://regex101.com/r/qZ6sE3/3
import re
p = re.compile(ur'([\d\s]+)(?=\*END)')
test_str = u"1 2 3 4\n 2 5 6 7\n3 8 9 10\n*END\n\n\n1 11 12 13\n2 14 15 16\n3 17 18 19\n*END"
re.findall(p, test_str)
编辑:
对于新的要求尝试
((?:\s*\d+(?:\s+\d+){4}\s*)*)(?=\s*\*END)
参见演示。
答案 4 :(得分:0)
这对你有用,
import re
import sys
with open('file.txt', 'r') as f:
file = f.read()
file = re.sub(r'(?s)\n\*END$', r'', file)
print(re.split(r'\n\*END\n+', file))
输出:
['1 2 3 4\n 2 5 6 7\n3 8 9 10', '1 11 12 13\n2 14 15 16\n3 17 18 19\n']