4.2 KiB
Python 的真实世界正则表达式
原文:https://www.pythoncentral.io/real-world-regular-expressions-for-python/
我们已经在这一系列文章中讨论了很多内容,所以现在让我们把它们放在一起,并通过一个实际应用程序来工作。
一个常见的任务是解析 Windows INI 文件,这些文件是键/值对,分成几个部分,如下所示:
[Section 1]
val1=hello world
val2=42
[Section 2]
val1=foo!
让我们首先编写一段 Python 代码,逐行读入测试文件:
for lineBuf in open('test.ini', 'r'):
print(lineBuf)
现在,我们将通过编写一些正则表达式来计算每行内容,从而扩展这一点。
识别章节标题
我们要做的第一件事是编写一个正则表达式,它将识别部分标题,即以方括号开始和结束的行。我们可以这样写一个正则表达式:^\[(.+)\]$
用简单的英语说:
- 匹配
^(行首)。 - 匹配一个
[字符(转义,因为[通常在正则表达式中有特殊含义)。 - 匹配在组中捕获的一个或多个字符(部分名称)。
- 匹配一个
]字符(其实没必要转义这个)。 - 匹配
$(行尾)。
如果我们更新代码来使用这个正则表达式:
sectionRegEx = re.compile(r'^\[(.+)\]$')
for lineBuf in open('test.ini', 'r'):
mo = sectionRegEx.search(lineBuf)
if mo:
print('Found a section: [%s]' % mo.group(1))
我们得到这样的输出:
Found a section: [Section 1]
Found a section: [Section 2]
似乎工作正常!
处理节标题中的空白
处理节标题中的空白会很方便,所以如果有人给我们一个如下所示的 INI 文件:
[Section 1]
val1=hello world
val2=42
[ Section 2 ] junk here!
val1=foo!
我们将能够正确处理奇怪的第二部分标题。现在,我们的代码没有找到它,所以让我们更新正则表达式来处理它:^\s*\[\s*(.+?)\s*\]
用简单的英语说:
- 匹配
^(行首)。 - 匹配
\s*(零个或多个空白字符)。 - 匹配一个
[字符。 - 匹配
\s*(零个或多个空白字符)。 - 匹配一个或多个字符(部分名称)。
- 匹配
\s*(零个或多个空白字符)。 - 匹配一个
]字符。
请注意,我们必须使+字符(捕获部分名称)非贪婪,以防止它匹配任何可能出现在结束]之前的尾随空格。我们也在结束]后停止匹配,因为我们不关心在它之后的线上是否有任何东西。
现在,我们的代码识别了格式古怪的节名:
sectionRegEx = re.compile(r'^\s*\[\s*(.+?)\s*\]')
for lineBuf in open('test.ini', 'r'):
mo = sectionRegEx.search(lineBuf)
if mo :
print('Found a section: [%s]' % mo.group(1))
这为我们提供了以下输出:
Found a section: [Section 1]
Found a section: [Section 2]
找到了第二个部分的标题,并清除了它的名称。
识别键/值对
下一步是编写一个识别键/值对的正则表达式,可能是这样的:^(.+)=(.+)$
用简单的英语说:
- 匹配
^(行首)。 - 匹配在组中捕获的一个或多个字符(密钥名)。
- 匹配
=字符。 - 匹配在组中捕获的一个或多个字符(键值)。
- 匹配
$(行尾)。
同样,我们希望这个正则表达式处理无关的空白,所以让我们把它改写成这样:^\s*(.+?)\s*=\s*(.+?)\s*$
我们更新后的代码现在看起来像这样:
sectionRegEx = re.compile(r'^\s*\[\s*(.+?)\s*\]')
keyValRegEx = re.compile(r'^\s*(.+?)\s*=\s*(.+?)\s*$')
for lineBuf in open('test.ini', 'r'):
mo = sectionRegEx.search(lineBuf)
if mo:
print('Found a section: [%s]' % mo.group(1))
mo = keyValRegEx.search(lineBuf)
if mo:
print('{%s} = {%s}' % (mo.group(1), mo.group(2)))
当我们打印键名和值时,我们用花括号将它们括起来,这样我们就可以看到它们是否被正确地剪裁了。
如果我们给它以下测试输入:
[Section 1]
val1=hello world
val2 = 42 = forty-two
[ Section 2 ] junk here!
val1=foo!
我们得到以下输出:
Found a section: [Section 1]
{val1} = {hello world}
{val2} = {42 = forty-two}
Found a section: [Section 2]
{val1} = {foo!}