Python: 类似awk的匹配之间的连续行
Python: consecutive lines between matches similar to awk
给定:
- 多行字符串
string
(已从文件file
读取)
- 两个模式
pattern1
和 pattern2
将分别匹配 string
中恰好一行的子字符串。这些行将称为 line1 和 line2。
模式是正则表达式模式,但如果这样更容易,我可以更改它们的格式。
已搜索
我正在寻找一种方法来获取 python 中第 1 行和第 2 行之间的所有行(我们可以安全地假设第 1 行在第 2 行之前)。
当然这可以在 for 循环中完成,标志由 pattern1
设置并在 pattern2
匹配时中断。不过,我正在寻找更 compact 的解决方案。这是 awk
:
中的一个简单的单行代码
awk '/pattern1/,/pattern2/' file
示例:
文件:
aaa aa a
bbb bb b
ccc cc c
ddd dd d
eee ee e
fff ff f
模式 1:b bb
模式 2:d dd
期望的结果:
bbb bb b
ccc cc c
ddd dd d
使用 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
使用 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)
在 awk
中,/start/, /end/
范围正则表达式打印找到 /start/
的整行,直到并包括找到 /end/
模式的整行.它是一个有用的结构,已被 Perl、sed、Ruby 和其他人复制。
要在 Python 中执行范围运算符,请编写一个 class 来跟踪上一次调用 start
运算符的状态,直到 end
运算符.我们可以使用正则表达式(如 awk
所做的那样),或者可以将其简单地修改为返回一行数据的 True
或 False
状态的任何内容。
根据您的示例文件,您可以执行以下操作:
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 [=13=]} /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))
但这需要将整个文件读入内存。由于您声明字符串 has 已读入内存,因此在这种情况下这可能是最简单的:
''.join(re.findall(r'^.*b bb[\s\S]*d dd.*$', s, re.M))
给定:
- 多行字符串
string
(已从文件file
读取) - 两个模式
pattern1
和pattern2
将分别匹配string
中恰好一行的子字符串。这些行将称为 line1 和 line2。
模式是正则表达式模式,但如果这样更容易,我可以更改它们的格式。
已搜索
我正在寻找一种方法来获取 python 中第 1 行和第 2 行之间的所有行(我们可以安全地假设第 1 行在第 2 行之前)。
当然这可以在 for 循环中完成,标志由 pattern1
设置并在 pattern2
匹配时中断。不过,我正在寻找更 compact 的解决方案。这是 awk
:
awk '/pattern1/,/pattern2/' file
示例:
文件:
aaa aa a
bbb bb b
ccc cc c
ddd dd d
eee ee e
fff ff f
模式 1:b bb
模式 2:d dd
期望的结果:
bbb bb b
ccc cc c
ddd dd d
使用 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
使用 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)
在 awk
中,/start/, /end/
范围正则表达式打印找到 /start/
的整行,直到并包括找到 /end/
模式的整行.它是一个有用的结构,已被 Perl、sed、Ruby 和其他人复制。
要在 Python 中执行范围运算符,请编写一个 class 来跟踪上一次调用 start
运算符的状态,直到 end
运算符.我们可以使用正则表达式(如 awk
所做的那样),或者可以将其简单地修改为返回一行数据的 True
或 False
状态的任何内容。
根据您的示例文件,您可以执行以下操作:
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 [=13=]} /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))
但这需要将整个文件读入内存。由于您声明字符串 has 已读入内存,因此在这种情况下这可能是最简单的:
''.join(re.findall(r'^.*b bb[\s\S]*d dd.*$', s, re.M))