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

时间:2017-02-21 21:25:02

标签: python regex string awk regex-lookarounds

假设:

  • 多行字符串string(已从文件file中读取)
  • 两种模式pattern1pattern2,它们将匹配每个string中恰好一行的子字符串。这些行将被称为line1和line2。

模式是正则表达式模式,但如果这样可以更容易,我可以更改它们的格式。

搜索的

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

当然,这可以在一个for循环中完成,其中pattern1设置一个标志,pattern2匹配一个中断。不过,我在这里寻找更多 compact 解决方案。这是awk中的一个简单的oneliner:

awk '/pattern1/,/pattern2/' file

实施例

文件:

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

pattern1:b bb

pattern2:d dd

期望的结果:

bbb bb b
ccc cc c
ddd dd d

3 个答案:

答案 0 :(得分:1)

使用regex

>>> print(a)

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

预期结果:

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

或者只是附上的文字:

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

答案 1 :(得分:1)

使用re.DOTALL匹配任何内容,包括换行符。然后插入开始模式和结束模式:

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

注意:(1)string这里是您要搜索的文件或字符串。 (2)您需要import re。如果你真的想简明扼要,也许是故障点,你可以结合阅读文件并提取模式:

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

答案 2 :(得分:1)

awk /start/, /end/范围内,正则表达式打印找到/start/的整行,最多包括找到/end/模式的整行。它是一个有用的结构,并且已被Perl,sed,Ruby等复制。

要在Python中执行范围运算符,请编写一个类,以跟踪start运算符的上一次调用的状态,直到end运算符。我们可以使用正则表达式(如awk所做的那样),或者这可以简单地修改为返回数据行的TrueFalse状态的任何内容。

根据您的示例文件,您可以执行以下操作:

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)

打印:

bbb bb b
ccc cc c
ddd dd d

保留了逐行文件读取,并具有/start/,/end/正则表达式在其他语言中的灵活性。当然,您可以对多行​​字符串(假设名为s)执行相同的方法:

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

惯用法,在awk中,您可以使用标记获得与触发器相同的结果:

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

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

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  

也可以在内存字符串中使用。

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

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

Demo and explanation

但这需要将整个文件读入内存。既然你声明字符串已经被读入内存,那么在这种情况下这可能是最简单的:

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