使用Bash还是Python从文本文件中提取块

1 投票
4 回答
2827 浏览
提问于 2025-04-15 17:56

我有一个很大的文本文件,它的结构是这样的:

SEPARATOR
STRING1
(arbitrary number of lines)
SEPARATOR
...
SEPARATOR
STRING2
(arbitrary number of lines)
SEPARATOR
SEPARATOR
STRING3
(arbitrary number of lines)
SEPARATOR
....

文件中不同“块”之间唯一变化的就是STRING和分隔符之间的内容。我需要一个用bash或python写的脚本,给定一个输入STRING_i,输出一个文件,里面包含

SEPARATOR
STRING_i
(number of lines for this string)
SEPARATOR

在这里使用bash还是python,哪个方法更好?还有其他选择吗?这个过程也必须要快。

谢谢

4 个回答

0

如果你想让这个过程快一点,就得避免一次性读取整个文件来找到你需要的数据块。

  1. 先把文件读一遍,记录下每个STRING_I的起始位置(字节偏移量)和到下一个分隔符的距离(字节数)。你可以把这些记录放在一个单独的文件里,或者放在当前文件的“头部”。
  2. 每次查询STRING_I时,就读取这个记录。
    if STRING_I in index:
     file.seek( start_byte_location )
     file.read( length )
     return parse_with_any_of_procedures_above # like @gruszczy's doit() but w/o loop

不要把索引做得太复杂:用一个字典来表示STRING_I和它的位置、长度的关系,然后把这个字典用simplejson或pickle保存到文件里。

0

我会用Python写一些类似这样的代码:

import sys

file = open("file", "r")
counter = 0
count = False
for line in file:
  if count:
    counter += 1
  if count and SEPARATOR == line:
    break
  if not count and sys.argv[1] == line:
    count = True
print SEPARATOR, sys.argv[1], counter, SEPARATOR
file.close()
3

在Python 2.6或更高版本中:

def doit(inf, ouf, thestring, separator='SEPARATOR\n'):
  thestring += '\n'
  for line in inf:
    # here we're always at the start-of-block separator
    assert line == separator
    blockid = next(inf)
    if blockid == thestring:
      # found block of interest, use enumerate to count its lines
      for c, line in enumerate(inf):
        if line == separator: break
      assert line == separator
      # emit results and terminate function
      ouf.writelines((separator, thestring, '(%d)' % c, separator))
      inf.close()
      ouf.close()
      return
    # non-interesting block, just skip it
    for line in inf:
      if line == separator: break

在旧版本的Python中,你几乎可以做到同样的事情,但需要把这一行 blockid = next(inf) 改成 blockid = inf.next()

这里的假设是输入和输出文件是由调用者打开的(调用者还会传入有用的值,比如 thestring,以及可选的 separator),但这个函数的工作是关闭这些文件(例如,为了方便作为管道过滤器使用,输入是 sys.stdin,输出是 sys.stdout);当然,如果需要的话,这部分可以很容易地调整。

去掉 assert 语句会让程序运行得稍微快一点,但我喜欢它们的“合理性检查”作用(而且它们也有助于理解代码的逻辑流程)。

这个方法的关键在于文件是一个迭代器(按行读取),而迭代器可以在多个地方前进(所以我们可以有多个 for 循环,或者特定的“前进迭代器”的调用,比如 next(inf),它们能够很好地配合工作)。

撰写回答