5

我正在尝试在文件中寻找令牌':path,',然后将以下所有(任意数字计数)数字读取为数字(因此对于':path,123',我在文件中寻找,然后读取整数 123)。然后读取当前搜索位置和 pos+123 之间的字符(将它们存储在列表或其他任何内容中)。然后寻找 ':path' 的下一个匹配项,然后重复该过程。

我想要一个有点像的功能:

def fregseek(文件,current_seek,/regex/):

.
.
  value_found = ? # 在 :path,[0-9]+ 之后读取下一个 N 字符的结果
.
.
  返回 next_start_seek, value_found

一行中可能有任意数量的 ':path,' 匹配,并且该字符串可能出现在 ',' 之后指定的字符数内。我写了一堆乱七八糟的垃圾,每行都读到,然后每行 chomps 匹配所指示的前 N ​​个字符,然后继续处理字符串,直到它被全部吃完。然后读取下一个字符串,依此类推。

这太可怕了,当我真正需要做的就是寻找时,我不想不得不从一个潜在的巨大文件中删除所有行(特别是因为换行符是无关紧要的,所以有一个额外的处理步骤只是因为行很容易从文件中提取是荒谬的)。

所以,这就是我想要解决的问题。我需要寻找一个匹配,读取一个值,从该值的末尾继续寻找下一个匹配,依此类推,直到文件用完。

如果有人可以帮助我,我会很高兴收到他们的来信:)

如果可能,我想避免使用非标准库,我也想要最短的代码,但这是我最不关心的问题(速度和内存消耗是重要因素,但我不希望额外 50 loc 只是为了引导一些里面有一个小功能的图书馆,只要我知道它是什么,我就可以把它撕掉)。

我更喜欢 python 代码,但是,如果 perl 在这方面胜过 python,我将使用 perl,我也愿意接受聪明的 sed/awk/bash 脚本等,只要它们不是非常慢。

首先十分感谢。

4

2 回答 2

3

你可以在 python 中用近一行来完成:

with open('filename.txt') as f:
    text = f.read()

results = [text[i[0]:i[0] + i[1]] for i in 
           ((m.end(), int(m.group(1))) for m in
            re.finditer(':path,([0-9]+)', text))]

注:未经测试...

于 2012-09-26T21:55:58.727 回答
3

如果您不需要正则表达式,则只需查找和切片即可。

无论哪种方式,简单的解决方案是将整个文件读入内存,然后找到并切片结果str/bytes对象。

但是,如果您不能(或不想)将整个文件读入内存,这将不起作用。

幸运的是,如果您可以指望您的文件 << 2GB 或者您只需要在 64 位 Python 中工作,并且您在一个合理的平台(POSIX、现代 Windows 等)上,您可以mmap将文件而是进入记忆。该mmap对象具有与字符串相同的方法的一个子集,因此您可以假装您有一个字符串,就像您将整个文件读入内存一样,但是您可以依靠 Python 实现和操作系统来使它只是以合理的效率工作。

根据您的 Python 版本,re可能无法像扫描字符串一样扫描 mmap,它可能工作但速度很慢,或者工作得很好。所以,你不妨先尝试一下,如果它没有抛出异常或比你预期的慢得多,你就完成了:

def findpaths(fname):
    with open(fname, 'rb') as f:
        m = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
        for match in re.finditer(':path,([0-9]+)', m):
            yield m[match.end():match.end()+int(match.group(1))]

(这与 BrtH 的答案相同,只是使用 mmap 而不是字符串,并重组为生成器而不是列表——尽管你当然可以通过用括号替换他的方括号来完成后一部分。)

re如果您使用的是不能(有效) a的较旧(或非 CPython?)版本的 Python mmap,它会更复杂一些:

def nextdigits(s, start):
  return ''.join(itertools.takewhile(str.isdigit,
                                     itertools.islice(s, start, None)))

def findpaths(fname):
  with open(fname, 'rb') as f:
    m = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
    i = 0
    while True:
      n = m.find(':path', i)
      if n == -1: return
      countstr = nextdigits(m, n+6)
      count = int(countstr)
      n += 6 + len(countstr)
      yield m[n:n+count]
      i = n + 6 + count

这可能不是编写nextdigits函数的最快方法。我不确定这是否真的很重要(计时并查看),但如果确实如此,其他可能性是切出m[n+6:n+A_BIG_ENOUGH_NUMBER]并正则表达式,或编写自定义循环,或者......另一方面,如果这是你的瓶颈,你通过切换到具有 JIT(PyPy、Jython 或 IronPython)的解释器可能会获得更多好处……</p>

对于我的测试,我将事情分开:findpaths接受一个类似字符串的对象,调用者执行with openandmmap位,然后m传入findpaths; 我在这里不是为了简洁起见。

无论如何,我已经针对以下数据测试了这两个版本:

BLAH:path,3abcBLAH:path,10abcdefghijklmnBLAH:path,3abc:path,0:path,3abc

输出是:

abc
abcdefghij
abc

abc

我认为这是正确的?

如果我的早期版本导致它以 100% CPU 旋转,我的猜测是我没有i在循环中正确递增;这是您在紧密的解析循环中获得该行为的最常见原因。无论如何,如果您可以使用当前版本重现该内容,请发布数据。

于 2012-09-26T23:08:58.277 回答