在Python中,我刚刚从文本文件中读取了一行,我想知道如何编写代码来忽略以井号#开头的注释。
我认为应该像这样:
for
if line !contain #
then ...process line
else end for loop
但我是Python的新手,不熟悉语法。
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]
,我们仅获取分隔符之前的部分。
编辑:
如果您正在使用不带有partition()
函数的Python版本,则可以使用以下代码:
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指出'#'可以出现在字符串中。正确处理这种情况并不容易,因此我忽略了它,但我应该说一些东西。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
虽然我不想在一个标签为“初学者”的问题中变得复杂,但这个状态机相当简单,并且我希望它会很有趣。
partition()
只存在于 Python 2.5 及以上版本。我会编辑我的回答并添加另一种解决方案。 - stevehastr.split()
可能是更好的选择。我会使用可选的maxsplit
参数,将其设置为1,这样它就会在找到一个#
字符后停止,像这样:line.split('#', 1)[0]
。我真的很喜欢str.partition()
,并且我倾向于使用它,但是str.split()
在旧版本的Python中也可以使用。str.partition()
还会返回中间字符串,但我们在这里不需要它,所以没有理由使用它。如果str.partition()
不可用,我会编辑答案建议使用str.split()
。 - stevehafor line in whatever:
line = line.split('#',1)[0].strip()
if not line:
continue
# process line
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
shlex
的解决方案也比被接受的答案更全面(对于简单的用例来说,被接受的答案还是可以的,但问题在于如果你告诉用户“你可以在里面放注释,但只有在行首”,你会第一个忘记这个限制)。 - dlukesfor line in open(filename):
if line.startswith('#'):
continue
# PROCESS LINE HERE
对于字符串,startswith()
方法如果以传入的字符串开头则返回 True。
这在一些场景下还可以使用,比如在 shell 脚本中。但其有两个问题。首先,它没有指定如何打开文件。默认的文件打开模式是 'r'
,这意味着“以二进制模式读取文件”。由于你期望的是一个文本文件,最好使用 'rt'
打开它。虽然在类 UNIX 系统上这种区别无关紧要,但在 Windows (和早期的 Mac OS X) 上很重要。
第二个问题是打开的文件句柄。open()
函数返回一个文件对象,通常情况下,在使用完文件后应当关闭文件。为此,请在该对象上调用 close()
方法。现在,Python 可能会在某个时间点自动完成此操作,因为 Python 对象是引用计数的,当一个对象的引用计数降至 0 时,它将被释放,并且在 Python 调用其析构函数(一个名为 __del__
的特殊方法)后的某个时间点。需要注意的是,我说过“可能”:Python 经常不会真正地调用那些在线程结束前引用计数降至 0 的对象的析构函数。我想这是因为它着急完成。
对于短暂的 shell 脚本,特别是文件对象,这没有问题。当程序结束时,操作系统会自动清理掉任何打开的文件句柄。但是如果你打开了文件,读取了内容,然后开始长时间计算而没有显式关闭文件句柄,Python 在计算期间可能会保持文件句柄处于打开状态。这是不好的做法。
此版本适用于 Python 的任何 2.x 版本,并修复了我上述的两个问题:
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
if line.startswith('#'):
转换为:
if line.lstrip().startswith('#'):
line
的值。 lstrip()
方法返回一个将其所有前导空格都删除的字符串副本。我知道这是一个旧的线程,但这是一个生成器函数,我为自己的目的使用它。它会删除注释,无论注释出现在哪一行,还会删除前导/尾随空格和空白行。以下是源代码:
# 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))
常规用法是从文件中删除注释(例如,像上面我的示例中的hosts文件)。如果是这种情况,则上述代码的尾部将被修改为:
if __name__ == '__main__':
with open('aa.txt', 'r') as f:
hosts = strip_comments(f)
for host in hosts:
print('\'%s\'' % host)
最近我发现生成器函数非常适合这个任务。我曾经使用类似的函数来跳过注释行、空行等。
我的函数定义如下:
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
这段代码可以在我所有的程序中重复使用,我还可以添加任何需要的处理、日志记录等。
for line in (l for l in open(filename) if not l.startswith('#')):
# do something with line
(l for ... )
被称为 "生成器表达式",它在迭代文件时充当包装迭代器,可以过滤掉所有不需要的行。不要将其与方括号中的相同内容 [l for ... ]
混淆,这是一个 "列表推导式",它会首先将文件中的所有行读入内存,然后再开始迭代。
有时您可能希望让代码更加易读:
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的答案得到了我的赞同,因为它可以包含#之前的任何信息
def clear_coments(f):
new_text = ''
for line in f.readlines():
if "#" in line: line = line.split("#")[0]
new_text += line
return new_text
if not line.strip().startswith("#")
- exhumafor line in open("file"):
这行,它会保留一个打开的文件句柄。当你完成后,你应该保持open("file")
的返回值并显式地调用close()
方法或者使用with
语句(参见http://docs.python.org/library/stdtypes.html#file.close)。 - Mike Mazur