Python:读取文件时如何忽略#comment行
题
在Python中,我刚刚从文本文件中读取了一行,我想知道如何编写代码来忽略行开头带有哈希#的注释。
我认为应该是这样的:
for
if line !contain #
then ...process line
else end for loop
但我是Python新手,不知道语法
解决方案
可以使用 startswith()
例如
for line in open("file"):
li=line.strip()
if not li.startswith("#"):
print line.rstrip()
其他提示
我建议,当你看到一个#
字符,你不要忽略全线飘红;只是忽略该行的其余部分。可以用一个字符串方法函数调用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子(用[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
我真的不希望得到的标记为“新手”问题这个复杂,但这种状态机是相当简单的,我希望这将是有趣。
我在这么晚来,但处理的外壳风格(或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
这是最短的形式:
for line in open(filename):
if line.startswith('#'):
continue
# PROCESS LINE HERE
这 startswith()
如果您调用的字符串以您传入的字符串开头,则字符串上的方法将返回 True。
虽然这在某些情况下(例如 shell 脚本)没问题,但它有两个问题。首先,它没有指定如何打开文件。打开文件的默认模式是 'r'
, ,这意味着“以二进制模式读取文件”。由于您需要一个文本文件,因此最好使用以下命令打开它 'rt'
. 。尽管这种区别在类 UNIX 操作系统上无关紧要,但在 Windows(以及 OS X 之前的 Mac)上却很重要。
第二个问题是打开的文件句柄。这 open()
函数返回一个文件对象,使用完文件后关闭文件被认为是一个好习惯。为此,请致电 close()
对象上的方法。现在,Python 将 大概 为你做这件事, 最终; 在Python中,对象是引用计数的,当对象的引用计数为零时,它就会被释放,并且在对象被释放后的某个时刻,Python将调用它的析构函数(一种称为析构函数的特殊方法) __del__
)。注意我说的是 大概: Python 有一个坏习惯,即不会在程序结束前不久对引用计数降至零的对象实际调用析构函数。估计是着急了!
对于像 shell 脚本这样的短期程序,特别是文件对象,这并不重要。当程序完成时,您的操作系统将自动清理所有打开的文件句柄。但是,如果您打开文件,读取内容,然后开始长时间计算,而没有先显式关闭文件句柄,Python 可能会在计算期间使文件句柄保持打开状态。这是不好的做法。
该版本适用于任何 2.x 版本的 Python,并修复了我上面讨论的两个问题:
f = open(file, 'rt')
for line in f:
if line.startswith('#'):
continue
# PROCESS LINE HERE
f.close()
这是旧版本 Python 的最佳通用形式。
正如 steveha 所建议的,使用“with”语句现在被认为是最佳实践。如果你使用的是 2.6 或更高版本,你应该这样写:
with open(filename, 'rt') as f:
for line in f:
if line.startswith('#'):
continue
# PROCESS LINE HERE
“with”语句将为您清理文件句柄。
在你的问题中你说“以#开头的行”,所以这就是我在这里向你展示的内容。如果你想过滤掉以 可选的空白 和 然后 '#',您应该在查找 '#' 之前去除空格。在这种情况下,您应该更改此设置:
if line.startswith('#'):
对此:
if line.lstrip().startswith('#'):
在Python中,字符串是不可变的,因此这不会改变 line
. 。这 lstrip()
方法返回删除了所有前导空格的字符串副本。
我最近发现,一台发电机的功能做的这个伟大的工作。我已经使用类似的功能来跳过注释行,空行,等等。
我定义我的功能
def skip_comments(file):
for line in file:
if not line.strip().startswith('#'):
yield line
这样的话,我可以做
f = open('testfile')
for line in skip_comments(f):
print line
这是在我的所有代码可重复使用的,我可以添加任何额外的处理/记录/等。我需要。
我知道这是一个古老的线程,但是这是一个发电机功能,我 用我自己的目的。这条评论无论身在何处 出现在线路,以及汽提前/后空白和 空行。下面的源文本:
# Comment line 1
# Comment line 2
# host01 # This host commented out.
host02 # This host not commented out.
host03
host04 # Oops! Included leading whitespace in error!
将产生:
host02
host03
host04
下面是记录代码,其包括演示:
def strip_comments(item, *, token='#'):
"""Generator. Strips comments and whitespace from input lines.
This generator strips comments, leading/trailing whitespace, and
blank lines from its input.
Arguments:
item (obj): Object to strip comments from.
token (str, optional): Comment delimiter. Defaults to ``#``.
Yields:
str: Next uncommented non-blank line from ``item`` with
comments and leading/trailing whitespace stripped.
"""
for line in item:
s = line.split(token, 1)[0].strip()
if s:
yield s
if __name__ == '__main__':
HOSTS = """# Comment line 1
# Comment line 2
# host01 # This host commented out.
host02 # This host not commented out.
host03
host04 # Oops! Included leading whitespace in error!""".split('\n')
hosts = strip_comments(HOSTS)
print('\n'.join(h for h in hosts))
在正常使用情况下,将是从文件剥离意见(例如,主机文件,如在上面的示例所示)。如果是这种情况,那么上述代码的尾端将被修改为:
if __name__ == '__main__':
with open('hosts.txt', 'r') as f:
hosts = strip_comments(f)
for host in hosts:
print('\'%s\'' % host)
滤波表达的更紧凑的版本也可以是这样的:
for line in (l for l in open(filename) if not l.startswith('#')):
# do something with line
(l for ... )
被称为“发电机表述”在此充当一个包装迭代而遍历它将从文件滤除所有不需要的线路。不要用方brakets [l for ... ]
同样的事情,这是一个“列表理解”,将首先从文件中读取所有行到内存中,然后才开始遍历它混淆。
有时你可能想要拥有它少一个liney,更具可读性:
lines = open(filename)
lines = (l for l in lines if ... )
# more filters and mappings you might want
for line in lines:
# do something with line
所有的过滤器将在运行在一次迭代中被执行。
使用正则表达式re.compile("^(?:\s+)*#|(?:\s+)")
跳过新行和注释。
我倾向于使用
for line in lines:
if '#' not in line:
#do something
此将忽略整行,虽然其包括rpartition答案有我给予好评,因为它可以从#之前包括任何信息