关于regex:Python:匹配项之间的连续行类似于awk

Python: consecutive lines between matches similar to awk

鉴于:

  • 多行字符串string(已从文件file中读取)
  • 两种模式pattern1pattern2将分别匹配string中仅一行的子字符串。 这些行将称为line1和line2。

模式是正则表达式模式,但是如果可以简化的话,我可以更改其格式。

已搜寻

我正在寻找一种方法来获取python中line1和line2之间的所有行(我们可以安全地假设line1在line2之前)。

当然,这可以通过pattern1设置的标志和pattern2匹配时的中断在for循环中完成。 不过,我在这里寻找更紧凑的解决方案。 这是awk中的琐碎oneliner:

1
awk '/pattern1/,/pattern2/' file

例:

文件:

1
2
3
4
5
6
aaa aa a
bbb bb b
ccc cc c
ddd dd d
eee ee e
fff ff f

模式1:b bb

pattern2:d dd

期望的结果:

1
2
3
bbb bb b
ccc cc c
ddd dd d


awk中,/start/, /end/范围正则表达式将打印找到/start/的整行,直到发现/end/模式的整行。这是一个有用的构造,并已被Perl,sed,Ruby和其他人复制。

要在Python中执行范围运算符,请编写一个类来跟踪对start运算符直到end运算符的上一次调用的状态。我们可以使用正则表达式(如awk一样),也可以对其进行琐碎的修改以使其返回一行数据的TrueFalse状态。

给定示例文件,您可以执行以下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import re

class FlipFlop:
    ''' Class to imitate the bahavior of /start/, /end/ flip flop in awk '''
    def __init__(self, start_pattern, end_pattern):
        self.patterns = start_pattern, end_pattern
        self.state = False
    def __call__(self, st):
        ms=[e.search(st) for e in self.patterns]
        if all(m for m in ms):
            self.state = False
            return True
        rtr=True if self.state else False
        if ms[self.state]:
            self.state = not self.state
        return self.state or rtr

with open('/tmp/file') as f:
    ff=FlipFlop(re.compile('b bb'), re.compile('d dd'))
    print ''.join(line if ff(line) else"" for line in f)

印刷品:

1
2
3
bbb bb b
ccc cc c
ddd dd d

这样可以保留逐行读取的文件,并具有其他语言中/start/,/end/ regex的灵活性。当然,您可以对多行??字符串(假定命名为s)执行相同的方法:

1
2
''.join(line+"\
"
if ff(line) else"" for line in s.splitlines())

习惯上,在awk中,使用标志可以获得与触发器相同的结果:

1
$ awk '/b bb/{flag=1} flag{print $0} /d dd/{flag=0}' file

您也可以在Python中复制它(使用更多单词):

1
2
3
4
5
6
7
8
9
flag=False    
with open('file') as f:
    for line in f:
        if re.search(r'b bb', line):
            flag=True
        if flag:
            print(line.rstrip())
        if re.search(r'd dd', line):
            flag=False

也可以与内存字符串一起使用。

或者,您可以使用多行正则表达式:

1
2
with open('/tmp/file') as f:
    print ''.join(re.findall(r'^.*b bb[\\s\\S]*d dd.*$', f.read(), re.M))

演示与说明

但这需要将整个文件读入内存。由于您声明该字符串已被读入内存,因此在这种情况下这可能是最简单的:

1
''.join(re.findall(r'^.*b bb[\\s\\S]*d dd.*$', s, re.M))

使用re.DOTALL匹配包括换行符在内的所有内容。然后插入开始模式和结束模式:

1
re.search( '[\\w ]*b bb.*?d dd[ \\w]*', string, re.DOTALL).group(0)

注意:(1)string这是您要搜索的文件或字符串。 (2)您需要import re。如果您确实想简明扼要(也许是错误的意思),则可以结合读取文件和提取模式:

1
re.search( '[\\w ]*b bb.*?d dd[ \\w]*', open('file').read(), re.DOTALL).group(0)


使用regex

1
2
3
4
5
6
7
8
>>> print(a)

aaa aa a
bbb bb b
ccc cc c
ddd dd d
eee ee e
fff ff f

预期结果:

1
2
3
4
5
6
>>> print(re.search('^.*bb b$\
((:?.+\
)+)^.*dd d$'
,a, re.M).group())
bbb bb b
ccc cc c
ddd dd d

或仅附上文字:

1
2
3
4
>>> print(re.search('^.*bb b$\
((:?.+\
)+)^.*dd d$'
,a, re.M).group(1))
ccc cc c