使用python中的regex提取数值数据块

时间:2014-11-21 11:02:31

标签: python regex multiline

我正在尝试解析一个显示数字数据部分的文件。目标是提取每个数据块以进行进一步解析。事实是这些部分跨越多行(以\ 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

然而,结果并非如此。它匹配最后一行,但也有一些空字符串......理解有什么帮助吗?

此致 弗朗索瓦。

5 个答案:

答案 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)

参见演示。

http://regex101.com/r/qZ6sE3/9

答案 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']