Python:如何在读取文件时忽略#注释行
在Python中,我刚刚从一个文本文件中读取了一行内容,我想知道怎么写代码来忽略以#号开头的注释行。
我觉得应该像这样:
for
if line !contain #
then ...process line
else end for loop
但是我对Python还很陌生,不太懂语法。
10 个回答
我来得有点晚,但处理 shell 风格(或 Python 风格)的 #
注释的问题是一个非常常见的情况。
我几乎每次读取文本文件时都会用到一些代码。
问题是它不能正确处理带引号或转义的注释。不过对于简单的情况,它还是能用的,而且很简单。
for line in whatever:
line = line.split('#',1)[0].strip()
if not line:
continue
# process line
一个更可靠的解决方案是使用 shlex:
import shlex
for line in instream:
lex = shlex.shlex(line)
lex.whitespace = '' # if you want to strip newlines, use '\n'
line = ''.join(list(lex))
if not line:
continue
# process decommented line
这个 shlex 方法不仅能正确处理引号和转义字符,还增加了很多很酷的功能(比如如果你想的话,可以让文件引用其他文件)。我还没有在大文件上测试它的速度,但在小文件上它的速度还不错。
当你还需要把每一行输入按空格分割成字段时,情况会更简单:
import shlex
for line in instream:
fields = shlex.split(line, comments=True)
if not fields:
continue
# process list of fields
我建议你看到 #
字符时,不要忽略整行,只需忽略这一行的其余部分。你可以使用一个叫做 partition
的字符串方法来轻松做到这一点:
with open("filename") as f:
for line in f:
line = line.partition('#')[0]
line = line.rstrip()
# ... do something with line ...
partition
会返回一个元组:在分隔字符串之前的所有内容、分隔字符串本身,以及在分隔字符串之后的所有内容。所以,通过使用 [0]
来索引,我们可以只获取分隔字符串之前的部分。
编辑:
如果你使用的 Python 版本没有 partition()
方法,这里有一段代码可以使用:
with open("filename") as f:
for line in f:
line = line.split('#', 1)[0]
line = line.rstrip()
# ... do something with line ...
这段代码会在 #
字符上分割字符串,然后保留分割前的所有内容。1
这个参数让 .split()
方法在分割一次后停止;因为我们只是获取第一个子字符串(通过 [0]
索引),即使没有 1
参数也能得到相同的结果,不过这样可能会稍微快一点。(感谢 @gnr 的评论,我简化了我原来的代码,之前的代码有点乱,没必要;谢谢你,@gnr。)
你也可以自己写一个 partition()
的版本。这里有一个叫做 part()
的:
def part(s, s_part):
i0 = s.find(s_part)
i1 = i0 + len(s_part)
return (s[:i0], s[i0:i1], s[i1:])
@dalle 提到过 #
可能会出现在字符串内部。处理这种情况并不简单,所以我选择忽略它,但我应该提一下。
如果你的输入文件对带引号的字符串有简单的规则,这并不难。如果你接受任何合法的 Python 带引号字符串,那就会很麻烦,因为有单引号、双引号、多行引号(用反斜杠转义换行符)、三重引号(可以是单引号或双引号),甚至还有原始字符串!正确处理这些情况的唯一方法就是使用复杂的状态机。
但如果我们限制在简单的带引号字符串,我们可以用简单的状态机来处理。我们甚至可以允许在字符串内部使用反斜杠转义的双引号。
c_backslash = '\\'
c_dquote = '"'
c_comment = '#'
def chop_comment(line):
# a little state machine with two state varaibles:
in_quote = False # whether we are in a quoted string right now
backslash_escape = False # true if we just saw a backslash
for i, ch in enumerate(line):
if not in_quote and ch == c_comment:
# not in a quote, saw a '#', it's a comment. Chop it and return!
return line[:i]
elif backslash_escape:
# we must have just seen a backslash; reset that flag and continue
backslash_escape = False
elif in_quote and ch == c_backslash:
# we are in a quote and we see a backslash; escape next char
backslash_escape = True
elif ch == c_dquote:
in_quote = not in_quote
return line
我其实不想在一个标记为“初学者”的问题中搞得这么复杂,但这个状态机相对简单,我希望它会引起你的兴趣。
你可以使用 startswith() 这个方法。
比如:
for line in open("file"):
li=line.strip()
if not li.startswith("#"):
print line.rstrip()