Python:读取文件时如何忽略#comment行

     2023-02-21     250

关键词:

【中文标题】Python:读取文件时如何忽略#comment行【英文标题】:Python: How to ignore #comment lines when reading in a file 【发布时间】:2010-12-14 22:33:38 【问题描述】:

在 Python 中,我刚刚从文本文件中读取了一行,我想知道如何编写代码以忽略行首带有哈希 # 的 cmets。

我觉得应该是这样的:

for 
   if line !contain #
      then ...process line
   else end for loop 

但我是 Python 新手,不懂语法

【问题讨论】:

【参考方案1】:

删除适用于内联和一行的评论是一件好事

def clear_coments(f):
    new_text = ''
    for line in f.readlines():
        if "#" in line: line = line.split("#")[0]

        new_text += line

    return new_text

【讨论】:

【参考方案2】:

我知道这是一个旧线程,但这是一个生成器函数,我 用于我自己的目的。无论他们在哪里,它都会剥夺 cmets 出现在行中,以及去除前导/尾随空格和 空行。以下原文:

# 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))

正常的用例是从文件(即主机文件,如我上面的示例中)中剥离 cmets。如果是这种情况,那么上面代码的尾部将被修改为:

if __name__ == '__main__':
    with open('hosts.txt', 'r') as f:
        hosts = strip_comments(f)

    for host in hosts:
        print('\'%s\'' % host)

【讨论】:

【参考方案3】:

使用正则表达式 re.compile("^(?:\s+)*#|(?:\s+)") 跳过新行和 cmets。

【讨论】:

【参考方案4】:

我来晚了,但是处理shell风格(或python风格)#cmets的问题是一个很常见的问题。

我几乎每次阅读文本文件时都在使用一些代码。问题是它不能正确处理引用或转义的 cmets。但它适用于简单的情况并且很容易。

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 

【讨论】:

这值得更多的支持!即使没有shlex 的解决方案也比公认的答案更全面(这对于简单的用例来说很好,但麻烦的是,如果你告诉用户“你可以把 c​​mets 放在那里,但前提是他们开始生产线”,你'会第一个忘记这个限制)。【参考方案5】:

我建议您在看到# 字符时不要忽略整行;只需忽略该行的其余部分。您可以使用名为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

我真的不想在一个标记为“初学者”的问题中搞得这么复杂,但是这个状态机相当简单,我希望它会很有趣。

【讨论】:

是的,但是如果您不考虑正确性,您可能还需要关心引用的#。 OP 需要注意的一点是旧版本中没有分区。 哎呀,没错:partition() 仅在 Python 2.5 和更新版本中。我将编辑我的答案并添加另一个解决方案。 为什么不能在这里使用 split 而不是 partition? @gnr,没有理由,str.split() 在这里可能是更好的选择。我会使用可选的maxsplit 参数,设置为1,所以它在找到一个# 字符后停止,如下所示:line.split('#', 1)[0] 我真的很喜欢str.partition(),我倾向于使用它,但是str.split() 可用在较旧的 Python 版本中。 str.partition() 还给你中间的字符串,但我们在这里不需要它,所以没有理由使用它。如果str.partition() 不可用,我将编辑答案以建议使用str.split()【参考方案6】:

我最近发现生成器函数在这方面做得很好。我使用过类似的功能来跳过注释行、空白行等。

我将我的函数定义为

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

这可以在我的所有代码中重复使用,并且我可以添加任何额外的处理/日志记录/等。我需要的。

【讨论】:

【参考方案7】:

我倾向于使用

for line  in lines:
    if '#' not in line:
        #do something

这将忽略整行,尽管包含 rpartition 的答案有我的支持,因为它可以包含 # 之前的任何信息

【讨论】:

【参考方案8】:

这是可能的最短形式:

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”语句将为您清理文件句柄。

在您的问题中,您说“以 # 开头的行”,这就是我在这里向您展示的内容。如果您想过滤掉以 optional whitespacethen '#' 开头的行,您应该在查找 '#' 之前去掉空格。在这种情况下,你应该改变这个:

    if line.startswith('#'):

到这里:

    if line.lstrip().startswith('#'):

在 Python 中,字符串是不可变的,所以这不会改变 line 的值。 lstrip() 方法返回字符串的副本,其中所有前导空格都已删除。

【讨论】:

“Python 有一个坏习惯,即在程序结束前不久对引用计数降至零的对象不调用析构函数。”你有这个说法的证据吗? “不能保证在解释器退出时为仍然存在的对象调用 del__() 方法。” __del__() 段落的最后一句:docs.python.org/reference/datamodel.html#object.__del 这是 2.6 的文档; 3.1 也是如此。我想我写的并不完全准确。然而,确切的事实与我提出的观点相关。不确定是否值得编辑我的答案以更正。【参考方案9】:

过滤表达式的更紧凑版本也可以如下所示:

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

所有过滤器将在一次迭代中即时执行。

【讨论】:

【参考方案10】:

你可以使用startswith()

例如

for line in open("file"):
    li=line.strip()
    if not li.startswith("#"):
        print line.rstrip()

【讨论】:

...同时忽略前导空格:if not line.strip().startswith("#") 您的代码有for line in open("file"):,它留下了一个打开的文件句柄。您应该保留open("file") 的返回值,并在完成后显式调用close(),或者使用with 语句(参见docs.python.org/library/stdtypes.html#file.close)。 不,不应该。当 EOF 时,for 循环将隐式调用 StopIteration。 这并没有真正留下打开的文件句柄,至少在 CPython 中是这样。当最后一次对文件对象的引用消失时,文件对象将被垃圾回收,此时文件将被关闭。 Jython(在 Java VM 上运行)可能有所不同。如果您使用的是具有with 语句的现代Python,则使用with open("filename") as f: 然后通过f(或您可能选择的任何其他变量名)引用文件对象被认为是一种非常好的形式。 with 将确保文件已关闭,无论如何,即使遇到异常也是如此。 我刚刚重新阅读了我上面的评论,我认为我的措辞很糟糕。我的示例使用f 作为with 语句中文件句柄的变量名,但您可以使用任何其他合法变量名。无论您在那里使用什么名称,都可以在 with 中使用来引用文件对象。很抱歉写的不清楚。

读取文件时Python中的UnicodeDecodeError,如何忽略错误并跳转到下一行?

】读取文件时Python中的UnicodeDecodeError,如何忽略错误并跳转到下一行?【英文标题】:UnicodeDecodeErrorinPythonwhenreadingafile,howtoignoretheerrorandjumptothenextline?【发布时间】:2014-08-2807:04:26【问题描述】:我必须将文本文件读入Python。文... 查看详情

读取 json 文件忽略自定义注释

...n文件忽略自定义注释【英文标题】:Readjsonfileignoringcustomcomments【发布时间】:2017-04-0218:54:04【问题描述】:我如何阅读这个文件\'file.json\':#Comment01#Comment02"name":"MyName"并在没有cmets的情况下检索json?我正在使用此代码:varfs=requ... 查看详情

java示例代码_:如何通过忽略"逐行读取文件; ";

java示例代码_:如何通过忽略"逐行读取文件; "; 查看详情

解析时忽略txt文件中的某些行

...间】:2019-03-2621:53:02【问题描述】:我想从一个txt文件中读取文件并将一些行与正则表达式进行比较。txt文件的第一行应以字符串#FIRST开头。如果字符串应以“#”开头,则应忽略该行并继续。所以计数器应该有它所做的值1,它... 查看详情

逐行清理文本文件,通过python忽略几行而不使用熊猫

】逐行清理文本文件,通过python忽略几行而不使用熊猫【英文标题】:cleaningtextfileeachlinebylineignoringfewrowsthroughpythonwithoutusingpandas【发布时间】:2021-08-0909:08:45【问题描述】:我想在python中使用这个文本文件,逐行读取,并希望... 查看详情

如何在 Python 中删除行尾? [复制]

】如何在Python中删除行尾?[复制]【英文标题】:HowdoIremoveendoflineinPython?[duplicate]【发布时间】:2015-07-1522:39:43【问题描述】:我发现每次读取一行时,也会包含行尾字符(在visetlist命令中显示为“$”),我想知道在读取文件时... 查看详情

python中如何读取文件

文本文件可存储的数据量多、每当需要分析或修改存储在文件中的信息时,读取文件都很有用,对数据分析应用程序处理文件,让程序能够快速地分析大量的数据处理文件和保存数据可让你的程序使用起来更容易一、从文件中读... 查看详情

我应该如何在 Python 中逐行读取文件?

】我应该如何在Python中逐行读取文件?【英文标题】:HowshouldIreadafileline-by-lineinPython?【发布时间】:2012-07-1807:41:35【问题描述】:在史前时代(Python1.4)我们做到了:fp=open(\'filename.txt\')while1:line=fp.readline()ifnotline:breakprintline在Pyt... 查看详情

Python:在代码中使用 PARALLEL 技术时如何忽略警告?

】Python:在代码中使用PARALLEL技术时如何忽略警告?【英文标题】:Python:howtoignorewarningswhenusePARALLELtechniquesincode?【发布时间】:2020-10-2718:03:03【问题描述】:我知道在Python中忽略警告的一种常见方法是使用:importwarningswarnings.filt... 查看详情

如何在读取时检测文件已被截断

...)中读取行,因为它们是使用pyinotify编写的。我正在使用python本地方法打开和读取文件:file=open(self.file_path,\'r\')#...laterline=file.readline() 查看详情

如何在文件行出现并将它们表示为 Flux 时读取它们?

】如何在文件行出现并将它们表示为Flux时读取它们?【英文标题】:HowtoreadfilelineswhiletheyappearandrepresentthemasFlux?【发布时间】:2020-09-1507:26:37【问题描述】:假设我们依赖于Reactor3(即在Spring5应用程序中)和一个文本文件my/file.t... 查看详情

java示例代码_在读取文本文件时忽略数字

java示例代码_在读取文本文件时忽略数字 查看详情

Python - 如何读取/解析 csv 行?

】Python-如何读取/解析csv行?【英文标题】:Python-howtoread/parsecsvlikeline?【发布时间】:2013-02-0614:46:41【问题描述】:我已经进行了一些搜索,但大多数答案都是关于阅读完整的csv文件,而这些都不像我面临的问题。我正在尝试使... 查看详情

mypy 如何忽略源文件中的一行?

...le?【发布时间】:2018-08-1913:48:40【问题描述】:我在我的python项目中使用mypy进行类型检查。我还使用PyYAML来读取和写入项目配置文件。不幸的是,当使用recommendedimportmechanismfromthePyYAMLdocumentation时,这会在尝试导入本机库的t 查看详情

如何读取文件的前 N ​​行?

...原始数据文件,我们希望将其修剪为指定的大小。如何在python中获取文本文件的前N​​行?正在使用的操作系统会对实现产生任何影响吗?【问题讨论】:我可以给n作为命令行参数吗【参考方案1】:Python2:withopen("datafile")asmyfi... 查看详情

MIRC,在读取文本文件时忽略“|”?

】MIRC,在读取文本文件时忽略“|”?【英文标题】:MIRC,Ignoring"|"whenreadingatextfile?【发布时间】:2016-07-2700:49:55【问题描述】:在我的MIRC脚本中,它被设置为读取一个文本文件,在这些文本文件中有符号“|”后跟一个空... 查看详情

python按行读取文件,如何去掉换行符""

python按行读取文件,如何去掉换行符"\\n" (2012-04-0221:27)标签:  python  readline  n 分类: python 点击(此处)折叠或打开for line in file.readlines():    line=line.strip('\\n')这里使用了strip去掉每行结束的\\n 查看详情

在 PIG 中加载文件时如何忽略“(双引号)?

...据"a","b","1","2""a","b","4","3""a","b","3","1"我正在使用以下命令读取此文件File1=LOAD\'/path\'usingPigStorage( 查看详情