提取特定的文字行?

时间:2010-07-14 16:35:59

标签: python

我有几个hudred千行文本文件。我必须在随机点中提取30,000个特定行,这些行都在文本文件中。这是我必须一次提取一行的程序:

big_file = open('C:\\gbigfile.txt', 'r')
small_file3 = open('C:\\small_file3.txt', 'w')
for line in big_file:
   if 'S0414' in line:
      small_file3.write(line)
gbigfile.close()
small_file3.close()

如何加快我需要查找的30,000行>?

10 个答案:

答案 0 :(得分:5)

啊哈!所以你真正的问题是如何测试每行的许多条件,如果其中一个满足,则输出该行。我认为最容易使用正则表达式:

import re
keywords = ['S0414', 'GT213', 'AT3423', 'PR342'] # etc - you probably get those from some source
pattern = re.compile('|'.join(keywords))

for line in inf:
    if pattern.search(ln):
        outf.write(line)

答案 1 :(得分:2)

使用朴素算法时,每行测试多个条件通常很慢。有各种优越的算法(例如使用Tries)可以做得更好。我建议你给Aho–Corasick string matching algorithm一个镜头。有关python实现,请参阅here。它应该比使用嵌套循环和单独测试每个字符串的天真方法快得多。

答案 2 :(得分:1)

根据Python的file objects文档,你所做的迭代不应该特别慢,搜索子字符串的速度也应该很快。

我没有看到你的代码速度慢的原因,所以如果你需要它更快,你可能需要在C中重写它并使用mmap()来快速访问源文件。

答案 3 :(得分:1)

<强> 1。尝试阅读整个文件

如果可能的话,你可以做的一个加速是在内存中读取整个文件,否则读取块。你说'几个hudred千行'可以说100万行,每行100个字符,即大约100 MB,如果你有那么多的空闲内存(我假设你有)只是这样做

big_file = open('C:\\gbigfile.txt', 'r')
big_file_lines = big_file.read_lines()
big_file.close()
small_file3 = open('C:\\small_file3.txt', 'w')
for line in big_file_lines:
   if 'S0414' in line:
      small_file3.write(line)
small_file3.close()

使用原始版本计算时间,看看它是否有所不同,我认为它会。

但是如果您的文件在GB中非常大,那么您可以用块读取它,例如100 MB块,将其拆分成行并搜索但不要忘记每隔100 MB间隔加入行(如果是这种情况,我可以详细说明)

file.readlines返回包含文件中所有数据行的列表。如果给定一个可选的参数sizehint,它会从文件读取多个字节,并且足以完成一行,并从中返回行。 这通常用于允许按行高效读取大文件,但无需将整个文件加载到内存中。只返回完整的行。

另请参阅以下链接,了解逐行与整个文件读取之间的速度差异。 http://handyfloss.wordpress.com/2008/02/15/python-speed-vs-memory-tradeoff-reading-files/

<强> 2。尝试编写整个文件

您也可以存储行并在结束时立即写入,但我不确定它是否会有所帮助

big_file = open('C:\\gbigfile.txt', 'r')
big_file_lines = big_file.read_lines()
small_file_lines = []
for line in big_file_lines:
   if 'S0414' in line:
      small_file_lines.append(line)
small_file3 = open('C:\\small_file3.txt', 'w')
small_file3.write("".join(small_file_lines))
small_file3.close()

第3。尝试过滤

您也可以尝试使用过滤器,而不是循环查看是否有差异

small_file_lines= filter(lambda line:line.find('S0414') >= 0, big_file_lines)

答案 4 :(得分:1)

您可以尝试大块读取,并避免除了特定的感兴趣线之外的线分割开销。例如,假设您的所有行都不超过兆字节:

BLOCKSIZE = 1024 * 1024

def byblock_fullines(f):
    tail = ''
    while True:
        block = f.read(BLOCKSIZE)
        if not block: break
        linend = block.rindex('\n')
        newtail = block[linend + 1:]
        block = tail + block[:linend + 1]
        tail = newtail
        yield block
    if tail: yield tail + '\n'

这需要一个打开的文件参数,并产生大约1MB的块,保证以换行结束。识别(以迭代器方式)在haystack字符串中出现的所有针头字符串:

def haystack_in_needle(haystack, needle):
    start = 0
    while True:
        where = haystack.find(needle, start)
        if where == -1: return
        yield where
        start = where + 1

从这样的区块中识别所有相关的线:

def wantlines_inblock(s, block):
    last_yielded = None
    for where in haystack_in_needle(block, s):
        prevend = block.rfind('\n', where)  # could be -1, that's OK
        if prevend == last_yielded: continue  # no double-yields
        linend = block.find('\n', where)
        if linend == -1: linend = len(block)
        yield block[prevend + 1: linend]
        last_yielded = prevend

这一切如何融合在一起:

def main():
    with open('bigfile.txt') as f:
        with open('smallfile.txt', 'w') as g:
            for block in byblock_fulllines(f):
                for line in wantlines_inblock('S0414', block)
                    f.write(line)

在2.7中你可以将两个with语句折叠成一个,只是为了减少嵌套。

注意:此代码未经测试,因此可能存在(希望很小;-)错误,例如逐个错误。性能需要调整块大小,并且必须通过对特定机器和数据的测量进行校准。你的旅费可能会改变。在法律禁止的地方无效。

答案 5 :(得分:0)

如果该行以S0414开头,那么您可以使用.startswith方法:

if line.startswith('S0414'): small_file3.write(line)

如果有的话,你也可以去掉左边的空格:

line.lstrip().startswith('S0414')

如果'S0414'总是出现在某个点之后,例如,它总是至少10个字符,而且从不在最后5个字符中,您可以这样做:

'S0414' in line[10:-5]

否则,你必须像你一样搜索每一行。

答案 6 :(得分:0)

定义要提取的30000行的标准是什么?您提供的信息越多,您获得有用答案的可能性就越大。

如果您希望包含特定字符串的所有行,或者更多通常包含任何给定字符串集或正则表达式的出现,请使用grep。对于大型数据集,它可能会明显加快。

答案 7 :(得分:0)

这让我想起了Tim Bray描述的问题,他试图使用多核机器从Web服务器日志文件中提取数据。结果在The Wide Finder ProjectWide Finder 2中有所描述。因此,如果串行优化不够快,那么这可能是一个开始的地方。有许多语言的例子,including python。最后一个链接的关键引用:

  

摘要

     

在本文中,我们使用了一个相对快速的Python实现并对其进行了优化   技巧数量:

     
      
  • 预编译的RE模式
  •   
  • 快速过滤候选线
  •   
  • Chunked reading
  •   
  • 多个流程
  •   
  • 内存映射,以及对映射缓冲区上的RE操作的支持
  •   
     

这减少了将200兆字节的日志数据从6.7秒解析为0.8所需的时间   测试机器上的秒数。或者换句话说,最终版本的速度提高了8倍多   比原始的Python版本,并且(可能)比Tim的原始版本快600倍   Erlang版本。

话虽如此,30,000行 很多,所以你可能想要至少从调查磁盘读/写性能开始。如果您将输出写入正在读取输入的磁盘以外的其他内容或在处理之前一次性读取整个文件,这会有帮助吗?

答案 8 :(得分:0)

加速它的最佳选择是,如果特定字符串S0414总是出现在相同的字符位置,那么不必每行进行几次失败的比较(你说它们以不同的名字开头)可以做一个并完成。

e.g。如果你的文件有像

这样的行
GLY S0414 GCT
ASP S0435 AGG
LEU S0432 CCT

做一个if line[4:9] == 'S0414': small.write(line)

答案 9 :(得分:0)

此方法假设特殊值显示在gbigfile

中该行的相同位置
def mydict(iterable):
    d = {}
    for k, v in iterable:
        if k in d:
            d[k].append(v)
        else:
            d[k] = [v]
    return d

with open("C:\\to_find.txt", "r") as t:
    tofind = mydict([(x[0], x) for x in t.readlines()])

with open("C:\\gbigfile.txt", "r") as bigfile:
    with open("C:\\outfile.txt", "w") as outfile:
        for line in bigfile:
            seq = line[4:9]
            if seq in tofind[seq[0]]:
                outfile.write(line)

根据这些目标中起始字母的分布情况,您可以大幅减少比较。如果你不知道这些值会出现在哪里,你就会谈论一个长时间的操作,因为你需要比较数十万 - 比如300,000 - 30,000次。这是900万次比较,这需要时间。