【问题标题】:Selecting line from file by using "startswith" and "next" commands使用“startswith”和“next”命令从文件中选择行
【发布时间】:2021-05-03 18:47:49
【问题描述】:

我有一个文件,我想根据每行“ITEM: TIMESTEP”之后出现的数字创建一个列表(“timestep”),所以:

timestep = [253400, 253500, .. etc]

这是我拥有的文件示例:

ITEM: TIMESTEP
253400
ITEM: NUMBER OF ATOMS
378
ITEM: BOX BOUNDS pp pp pp
-2.6943709180241954e-01 5.6240920636804063e+01
-2.8194230631882372e-01 5.8851195163321044e+01
-2.7398090193568775e-01 5.7189372326936599e+01
ITEM: ATOMS id type q x y z 
16865 3 0 28.8028 1.81293 26.876 
16866 2 0 27.6753 2.22199 27.8362 
16867 2 0 26.8715 1.04115 28.4178 
16868 2 0 25.7503 1.42602 29.4002 
16869 2 0 24.8716 0.25569 29.8897 
16870 3 0 23.7129 0.593415 30.8357 
16871 3 0 11.9253 -0.270359 31.7252 
ITEM: TIMESTEP
253500
ITEM: NUMBER OF ATOMS
378
ITEM: BOX BOUNDS pp pp pp
-2.6943709180241954e-01 5.6240920636804063e+01
-2.8194230631882372e-01 5.8851195163321044e+01
-2.7398090193568775e-01 5.7189372326936599e+01
ITEM: ATOMS id type q x y z 
16865 3 0 28.8028 1.81293 26.876 
16866 2 0 27.6753 2.22199 27.8362 
16867 2 0 26.8715 1.04115 28.4178 
16868 2 0 25.7503 1.42602 29.4002 
16869 2 0 24.8716 0.25569 29.8897 
16870 3 0 23.7129 0.593415 30.8357 
16871 3 0 11.9253 -0.270359 31.7252

为此,我尝试同时使用“startswith”和“next”命令,但没有成功。还有其他方法吗?我还发送了我尝试使用的代码:


timestep = []
with open(file, 'r') as f:
    lines = f.readlines()
    for line in lines:
        line = line.split()
        if line[0].startswith("ITEM: TIMESTEP"):
            timestep.append(next(line))     
            print(timestep)


【问题讨论】:

    标签: python readlines startswith


    【解决方案1】:

    逻辑是决定是否将当前line附加到timestep。因此,您需要的是一个变量,它告诉您在该变量为 TRUE 时附加当前的 line

    timestep = []
    append_to_list = False # decision variable
    
    with open(file, 'r') as f:
        lines = f.readlines()
        for line in lines:
            line = line.strip() # remove "\n" from line
            if line.startswith("ITEM"):
                # Update add_to_list
                if line == 'ITEM: TIMESTEP':
                    append_to_list = True
                else:
                    append_to_list = False
            else:
                # append to list if line doesn't start with "ITEM" and append_to_list is TRUE
                if append_to_list:
                    timestep.append(line)
    print(timestep)
    

    输出:

    ['253400', '253500']
    
    

    【讨论】:

      【解决方案2】:

      首先 - 我不喜欢这样,因为它无法扩展。您只能很好地获得第一条紧随其后的行,其他任何东西都只是呃......

      但是你问了,所以...for x in lines 将在行上创建一个迭代器并使用它来保持位置。您无权访问该迭代器,因此 next 不会是您期望的下一个元素。但是您可以制作自己的迭代器并使用它:

      lines_iter = iter(lines)
      for line in lines_iter:
          # whatever was here
                      timestep.append(next(line_iter))     
      

      但是,如果您想对其进行缩放...for 不是迭代此类文件的好方法。您想知道下一行/上一行是什么。我建议使用while:

      timestep = []
      with open('example.txt', 'r') as f:
          lines = f.readlines()
      i = 0
      while i < len(lines):
          if line[i].startswith("ITEM: TIMESTEP"):
             i += 1
             while not line[i].startswith("ITEM: "):
                 timestep.append(next(line))     
                 i += 1
          else:
             i += 1
      

      通过这种方式,您可以将其扩展为不同类型的可变长度项目。

      【讨论】:

        【解决方案3】:

        所以你的代码问题很微妙。您有一个列表 lines 进行迭代,但您不能在列表上调用 next

        相反,将其转换为显式迭代器就可以了

        timestep = []
        with open(file, 'r') as f:
            lines = f.readlines()
            lines_iter = iter(lines)
            for line in lines_iter:
                line = line.strip()  # removes the newline
                if line.startswith("ITEM: TIMESTEP"):
                    timestep.append(next(lines_iter, None))  # the second argument here prevents errors
                                                             # when ITEM: TIMESTEP appears as the
                                                             # last line in the file
                print(timestep)
        

        我也不确定你为什么包含line.split,这似乎是不正确的(无论如何line.split()[0].startswith('ITEM: TIMESTEP') 永远不会是真的,因为拆分会将ITEM:TIMESTEP 分成单独的元素结果列表。)


        要获得更可靠的答案,请考虑根据行以ITEM 开头的时间对数据进行分组。

        def process_file(f):
            ITEM_MARKER = 'ITEM: '
            item_title = '(none)'
            values = []
            for line in f:
                if line.startswith(ITEM_MARKER):
                    if values:
                        yield (item_title, values)
                    item_title = line[len(ITEM_MARKER):].strip()  # strip off the marker
                    values = []
                else:
                    values.append(line.strip())
            if values:
                yield (item_title, values)
        

        这将让您传入整个文件,并为每个ITEM: &lt;whatever&gt; 组懒惰地生成一组值。然后你可以以某种合理的方式聚合。

        with open(file, 'r') as f:
            groups = process_file(f)
        aggregations = {}
        for name, values in groups:
            aggregations.setdefault(name, []).extend(values)
        print(aggregations['TIMESTEP'])  # this is what you want
        

        【讨论】:

        • 请注意,您可能会完全跳过f.readlines,而只需对f进行操作
        • 有一个 split 命令,因为我想避免在每个列表元素之后有 '\n'。我知道 strip() 也可以使用,所以我和他们一起玩只是为了看看它会如何影响结果。
        • @kata248 我添加了一个关于使它成为更强大的解决方案的部分。
        【解决方案4】:

        您可以使用 enumerate 来帮助进行索引引用。我们可以检查字符串ITEM: TIMESTEP 是否在前一行中,然后将整数添加到我们的时间步列表中。

        timestep = []
        with open('example.txt', 'r') as f:
            lines = f.readlines()
            for i, line in enumerate(lines):
                if "ITEM: TIMESTEP" in lines[i-1]:
                    timestep.append(int(line.strip()))
                    print(timestep)
        

        【讨论】:

        • 感谢您的回答,我也将您的代码用于其他目的。你能告诉我如何从某些行(例如“ITEM:TIMESTEP”)附加所有之后的行吗?它可以是每行的字符串形式。我问是因为我想在您编写的代码中添加/修改一段代码。
        • 你可以创建一个布尔值 flag 并将其从 False 更改为 True,如果已经看到 "ITEM: TIMESTEP"
        猜你喜欢
        • 1970-01-01
        • 2020-09-03
        • 2023-03-13
        • 1970-01-01
        • 2014-11-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多