【问题标题】:Get text between two identical lines in Python [closed]在Python中的两条相同行之间获取文本[关闭]
【发布时间】:2015-03-06 12:03:16
【问题描述】:

我对 Python 完全陌生。我经常使用 Perl,听说 Python 通常更擅长解析文本,所以我想尝试一下,但我想不出最简单的方法来做到这一点(关于信息,我已经在 Perl 中做过,但是花了我几个,缓慢而丑陋的循环):

我想读取一个大文件并提取以相同模式开头的两行之间的文本块,例如:

!NAME: "N0",                DESCR: "Netnt Etrnet"
!NAME: "cp0",              DESCR: "Cle R0"
!NAME: "slt R1",               DESCR: "RSt"
>>!NAME: "moe R1",             DESCR: "ASessor 1,bps"
>>!PID: A9-55
>>!VID: G0984981
>>!SN: SEDGH25443N51E
!NAME: "SDFGSDFG: FGT/0",       DESCR: "VFDFGX1"
!NAME: "JQFHF1",       DESCR: "VNQDF2"

当然“>>”不是文本文件的一部分,它只是为了显示我想要检索的行。

所以回顾一下:我想打印所有块(文件中有更多块),其中块的第一行以“!NAME”开头,并且在下一个“!NAME”之前有其他行。

我不关心连续有两个“!NAME:”的块。

这只是第一步,稍后我将尝试检索此块的值以创建散列(或字典或任何与 python 中的散列等效的东西)。但是我已经卡在第一步了,所以我在寻求帮助哈哈。

谢谢!

【问题讨论】:

  • python 在很多方面都比 perl 好。解析文本不是其中之一。
  • 也许你应该改变在 Perl 中做这件事的方式。
  • Perl 非常适合处理文本。这几乎就是它存在的理由
  • 我收到评论说 Perl 擅长处理文本。确实如此,而且我从未说过其他话。我说我听说(从同事等处)python 有时会更好。所以我想试一试。我真的没有得到反对票......
  • 这就是我现在正在做的事情。我正在使用我用 perl 制作的脚本(我同意,它可能没有优化到最大值),并尝试将其转换为 Python。我会再做几个,这样我会自己看看一个比另一个更好(在我看来)做不同类型的事情

标签: python regex parsing


【解决方案1】:
with open("in.txt") as f:
    prev = ""
    for line in f:
        if not line.startswith("!NAME:"):
            print(prev.rstrip())
            print(line.rstrip())
            for line in f:
                if line.startswith("!NAME:"):
                    prev = line
                    break
                print(line.rstrip())
                prev = line
        prev = line

如果你想存储每个部分,你可以使用字典:

from itertools import count

from collections import defaultdict
cn = count()

sections = defaultdict(str)
with open("log.txt") as f:  
    prev = ""
    for line in f:
        if not line.startswith("!NAME:"):
            key = next(cn)
            sections[key] += prev
            sections[key] += line
            for line in f:
                if line.startswith("!NAME:"):
                    break
                 sections[key] += line
                 prev = line
        prev = line

print(d)
defaultdict(<class 'str'>, {0: '!NAME: "moe R1",             DESCR: "ASessor 1,bps"\n!PID: A9-55\n!VID: G0984981\n!SN: SEDGH25443N51E\n'})

为确保您只找到前面有 !Name 的部分,请确保前一行以 !Name: 开头:

with open("log.txt") as f:
    prev = ""
    for line in f:
        if not line.startswith("!NAME:") and prev.startswith("!NAME:"):
            key = next(cn)
            sections[key] += prev
            sections[key] += line
            for line in f:
                if line.startswith("!NAME:"):
                    break
                sections[key] += line
                prev = line
        prev = line

【讨论】:

  • 根据帖子中的&gt;&gt;,OP 似乎想要第一个!NAME
  • @JonClements,是的,我知道我最初出于某种原因曾去过那里!
  • 谢谢,乍一看,我认为这是我正在寻找的东西,即使我得到了错误(print(prev.rstrip())NameError:名称'prev'未定义)。我会尝试解决它,一旦我会验证你的答案:)
  • @user2407268,在我们第一次看到 !Name 之前有一行,所以 prev 从未定义,我添加了 prev = "" 但使用该逻辑有一个部分前面没有 @ 987654328@线
  • @Padraic 哈哈哈......你不知道你所有的想象中的互联网积分都属于我吗? :p (更严肃地说,我做了@ OP 来看看,但这主要是因为我认为你会发现这是一种有趣的方式......)
【解决方案2】:

或者,您可以使用itertools

  • 忽略文件中的所有内容,直到第一个!NAME
  • 按行是否以!NAME 开头进行分组
  • 将其分组,其中第一对是 !NAME 行,第二对是直到下一个 !NAME 或 EOF 之前的所有内容
  • 在输出中包含!NAME 行的最后一项,其后至少有一行不是!NAME

代码:

from itertools import groupby, izip_longest, dropwhile

with open('inputfile') as fin:
    stripped = (line.strip() for line in fin)
    start_at = dropwhile(lambda L: not L.startswith('!NAME'), stripped)
    grouped = (list(g) for k, g in groupby(start_at, lambda L: L.startswith('!NAME')))
    for name, rest in izip_longest(*iter([grouped] * 2), fillvalue=[]):
        if rest:
            print name[-1]
            print '\n'.join(rest)

给予:

!NAME: "moe R1",             DESCR: "ASessor 1,bps"
!PID: A9-55
!VID: G0984981
!SN: SEDGH25443N51E

【讨论】:

  • @user2407268 哈哈...够公平的...还是不明白您的意思...也许只是描述您要实现的逻辑?
  • 新评论,因为旧评论很丑,我没有意识到我不能在这里缩进文本。所以基本上你做了一个 dropwhile 函数,只在出现 !Name 时才启动,但是由于文件后面有我想忽略的行,我将它替换为 --- takewhile(lambda L: L.startswith("!Name", “!VID”,“!PID”,“!SN”),剥离)----问题是我收到一个错误,说startswith最多需要3个参数。有什么想法强制第四个参数吗?
  • @user2407268 str.startswith 接受一个元组 - 例如L.startswith(('!Name', '!VID', '!PID', '!SN'))....
  • @user2407268 我担心这会破坏逻辑......上面依赖于忽略每个直到第一个“!NAME”,然后配对是一个名字/不是一个名字。 ..所以虽然我仍然不确定你到底想做什么 - 小心那个;p
  • 嗯....您是否总是在连续行中查找名称 VID PID SN?
猜你喜欢
  • 2015-01-28
  • 1970-01-01
  • 2023-03-03
  • 1970-01-01
  • 2016-12-28
  • 1970-01-01
  • 2015-01-10
  • 2013-05-11
  • 2023-03-07
相关资源
最近更新 更多