Python反向引用正则表达式

4

我需要搜索类似以下内容的东西:

lines = """package p_dio_bfm is
   procedure setBFMCmd (  
      variable  pin : in tBFMCmd
      );
end p_dio_bfm; -- end package;

package body p_dio_bfm is
   procedure setBFMCmd (  
      variable  pin : in tBFMCmd
      ) is
   begin
      bfm_cmd := pin;
   end setBFMCmd;
end p_dio_bfm;"""

我需要提取包名,即p_dio_bfm,以及包声明,即在"package p_dio_bfm is"和第一个"end p_dio_bfm;"之间的部分。
问题在于包声明可能以"end p_dio_bfm;"或"end package;"结尾。因此,我尝试了以下的“OR”正则表达式: - 对于以"end package"结尾的包有效 - 对于以"end pck_name;"结尾的包无效
pattern = re.compile("package\s+(\w+)\s+is(.*)end\s+(package|\1)\s*;")
match = pattern.search(lines)

问题在于正则表达式中的(package|\1)部分,这里我想要匹配单词“package”或匹配到的包名。
更新:我提供了完整的代码,希望能够更清楚地说明。
import re
lines1 = """package p_dio_bfm is
   procedure setBFMCmd (
      variable  pin : in tBFMCmd
      );
end p_dio_bfm;

package body p_dio_bfm is
   procedure setBFMCmd (
      variable  pin : in tBFMCmd
      ) is
   begin
      bfm_cmd := pin;
   end setBFMCmd;
end p_dio_bfm;"""

lines2 = """package p_dio_bfm is
   procedure setBFMCmd (
      variable  pin : in tBFMCmd
      );
end package;

package body p_dio_bfm is
   procedure setBFMCmd (
      variable  pin : in tBFMCmd
      ) is
   begin
      bfm_cmd := pin;
   end setBFMCmd;
end package;"""

lines1 = lines1.replace('\n', ' ')
print lines1

pattern = re.compile("package\s+(\w+)\s+is(.*)end\s+(package|\1)\s*;")
match = pattern.search(lines1)

print match

lines2 = lines2.replace('\n', ' ')
print lines2

match = pattern.search(lines2)

print match

我希望在两种情况下,使用唯一的正则表达式,能够获取到这部分内容:

我期望无论哪种情况,都可以使用一个独特的正则表达式来获取这部分内容:

"""procedure setBFMCmd (
          variable  pin : in tBFMCmd
          );"""  

已经删除掉的\n字符。


您能发布期望的输出吗? - Iron Fist
2个回答

3

由于你的正则表达式不正确,所以它无法匹配任何内容。如果不使用多行标志.*无法匹配换行符,因此可以使用[\s\S]*代替:

r'package ([^\s]+)\s+is([\s\S]*)end\s+(package|\1)\s*;'

请查看演示https://regex101.com/r/tZ3uH0/1

但是这里还有另一个问题,你的字符串包含了 2 个包块。为了更加优雅和高效地解决此问题,你可以使用re.DOTALL 标志,使 '.' 特殊字符匹配任何字符,包括换行符。因此,你可以像下面这样编写你的正则表达式:

pattern = re.compile("package\s+(\w+)\s+is(.*)end\s+(package|\1)\s*;",re.DOTALL)

但是这仍然会匹配第一个区块:
>>> match = pattern.search(lines)
>>> print match.group(0)
package p_dio_bfm is
   procedure setBFMCmd (  
      variable  pin : in tBFMCmd
      );
end p_dio_bfm; -- end package;
>>> print match.group(1)
p_dio_bfm
>>> print match.group(2)

   procedure setBFMCmd (  
      variable  pin : in tBFMCmd
      );
end p_dio_bfm; -- 
>>> print match.group(3)
package

要匹配所有块,您需要在第二组中明确像body这样的单词:

package\s+(?:\w+\s+?)?([^\s]+)\s+is(.*?)end\s+(package|\1)\s*;

查看演示 https://regex101.com/r/tZ3uH0/3


我不明白在使用re.S时为什么要用[\s\S],而不是.。这里不需要处理特定的行匹配,对吗?除非需要将正则表达式迁移到JavaScript之类的语言,否则使用“内置”的方式匹配换行符更有效率。 - Wiktor Stribiżew
@Kasra,OP 没有说:在“package p_dio_bfm is”和第一个“end p_dio_bfm;”之间吗? - Iron Fist
你可以利用 named references(?P<needle>^\s+)\s...(package|(?P=needle)) - bufh
1
@stribizhev 确实,我正在编辑答案并添加更多信息,但作为起点,我建议那种方式! - Mazdak
1
@KhalilAmmour-خليلعمور 是的,这就是我将在更新中添加的要点! - Mazdak

2

你觉得如何:

>>> for row in re.findall(
...   r'package(?:\s.*?)(?P<needle>[^\s]+)\s+is\s+(.*?)end\s+(?:package|(?P=needle));',
...   lines,
...   re.S
... ):
...   print '{{{', row[1], '}}}'
...
{{{ procedure setBFMCmd (
      variable  pin : in tBFMCmd
      );
}}}
{{{ procedure setBFMCmd (
      variable  pin : in tBFMCmd
      ) is
   begin
      bfm_cmd := pin;
   end setBFMCmd;
}}}

我冒昧地没有按照 @mihai-hangiu 的要求过滤内容,同时包含了第二个块。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接