在Python中解析行:使用正则表达式还是不使用?
我是一名 Perl 程序员,现在想通过把以前做过的工作转成 Python 来学习 Python。这不是逐行翻译。我想学习的是Python 的技巧,来完成这种任务。
我正在解析一个 Windows 的 INI 文件。部分名称的格式是:
[<type> <description>]
这里的 <type>
是一个单词,不区分大小写。而 <description>
可以是多个单词。
在每个部分后面,有一堆参数和对应的值。它们的格式是:
<parameter> = <value>
参数中不能有空格,只能包含下划线、字母和数字(不区分大小写)。所以,第一个 =
是参数和对应值的分隔符。参数和值之间可能会有空格,也可能在行的开头或结尾有多余的空格。
在 Perl 中,我使用了正则表达式来解析:
while (my $line = <CONTROL_FILE>) {
chomp($line);
next if ($line =~ /^\s*[#;']/); #Comments start with "#", ";", or "'"
next if ($line =~ /^\s*$/); #Ignore blank lines
if ($line =~ /^\s*\[\s*(\w+)\s+(.*)/) { #Section
say "This is a '$1' section called '$2'";
}
elsif ($line =~ /^\s*(\w+)\s*=\s*(.*)/) { #Parameter
say "Parameter is '$1' with a value of '$2'";
}
else { #Not Comment, Section, or Parameter
say "Invalid line";
}
}
问题是我被 Perl 的思维方式影响了,所以我觉得最简单的方法就是用正则表达式。以下是我目前的代码……
for line in file_handle:
line = line.strip
# Comment lines and blank lines
if line.find("#") == 1 \
or line.find(";") == 1 \
or line.whitespace:
continue
# Found a Section Heading
if line.find("[") == 1:
print "I want to use a regular expression here"
print "to split the section up into two pieces"
elif line.find("=") != -1:
print "I want to use a regular expression here"
print "to split the parameter into key and value"
else
print "Invalid Line"
这里有几个让我烦恼的地方:
- 有两个地方似乎很适合用正则表达式。用 Python 来做这种分割的方式是什么?
- 我确保在字符串的两边去掉空格,并重新写这个字符串。这样我就不需要多次去掉空格了。不过,我知道在 Python 中重新写字符串是个效率很低的操作。用 Python 处理这个问题的正确方法是什么?
- 最后,我的算法看起来和我的 Perl 算法差不多,这让我觉得我在用 Perl 的思维方式来限制自己。我的 Python 代码应该怎么结构化?
我一直在看各种在线教程,它们帮助我理解了语法,但对如何处理语言本身,尤其是对一个习惯用另一种语言思考的人来说,帮助不大。
我的问题是:
- 我应该使用正则表达式吗?还是有其他更好的方法来处理这个?
- 我的编码逻辑正确吗?我应该如何思考来解析这个文件?
3 个回答
Python有一个可以处理ini文件的库,叫做ini解析库。如果你想自己做一个库来解析ini文件,那你就需要一个真正的解析器。光靠正则表达式是不够的,建议使用PLY,或者用C语言的flex/bison工具。还有其他的Python解析资源可以参考。
词法分析器(lexer)会帮你处理所有的文本读取和树结构构建,因为这些工作比较机械,容易出错。比如说这部分:
while (my $line = <CONTROL_FILE>) {
chomp($line);
next if ($line =~ /^\s*[#;']/); #Comments start with "#", ";", or "'"
next if ($line =~ /^\s*$/); #Ignore blank lines
if ($line =~ /^\s*\[\s*(\w+)\s+(.*)/) { #Section
say "This is a '$1' section called '$2'";
}
elsif ($line =~ /^\s*(\w+)\s*=\s*(.*)/) { #Parameter
say "Parameter is '$1' with a value of '$2'";
}
else { #Not Comment, Section, or Parameter
say "Invalid line";
}
}
就是由词法分析器生成的,你只需要定义正确的正则表达式。解析器会从词法分析器那里获取这些“标记”(tokens),并判断它们是否符合允许的标记模式。也就是说:
[<type> <description>]
<parameter> = <value>
你需要定义这些标记,以及它们如何组合在一起。其他的部分就会自动组合起来。对于那些觉得用简单的for循环和一些正则表达式就能做得更好的人,我建议你看看《Lex & Yacc, 第2版》。
如果你想看看我用PLY写的一个示例解析器,可以点击这里。它解析的是一种叫做“jetLetter”的文件,这其实是groff/troff的一种方言。
虽然我觉得这不是你的本意,但这个文件格式看起来和Python自带的ConfigParser模块挺像的。有时候,最“Pythonic”的方法其实已经为你准备好了。(:
直接回答你的问题:正则表达式可能是个不错的选择。否则,你也可以试试更基础(但不太强大)的方式。
(parameter, value) = line.split('=')
如果这一行没有'='字符或者有多个'='字符,这段代码会报错。你可能想先用'=' in line
来测试一下。
另外:
line.find("[") == 1
可能更好用下面这个来替代:
line.startswith("[")
希望这能帮到你一点(: