2010-07-14 56 views
5

我有一个很大的几个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() 

我怎么能加快这为30000行,我需要仰视>?

+1

我认为迄今为止所有的答案都误解了你的问题。如果我明白了,你不会加速你的循环。相反,您有30,000个字符串与'S0414'相似,并且想要找到找到每个字符串的方法。这是你在找什么? – Wilduck 2010-07-14 17:19:48

+0

我需要找到S0414和GT213以及AT3423和PR342,并且我需要找到30,000种不同的东西。我可以一次找到所有30,000,而不是让这个程序为我需要找到的每个名称都设置一行。我的计划长达30,000行,这是有问题的。 – novak 2010-07-14 18:41:48

+0

'S0414'和'GT213'等出现在Nick这样的大文件中的同一个地方吗? http://stackoverflow.com/questions/3248395/extract-specific-text-lines/3248603#3248603 – 2010-07-14 19:32:44

回答

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) 
+0

这是做到这一点的正确方法。感谢您指出我的答案是多么的错误。你可能想看看@ novak过去的问题,但看看我为什么试着用最简单的语法。具体来说,我对这个问题的答案http://stackoverflow.com/questions/3207719/question-about-splitting-a-large-file。最好的,威尔杜克。 – Wilduck 2010-07-14 19:35:37

+0

这很好,谢谢。 – novak 2010-07-14 22:10:36

0

如果符合S0414开始,那么你可以使用.startswith方法:

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

你也可以剥离左侧空白,如果有任何:

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

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

'S0414' in line[10:-5] 

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

+0

他们都以不同名称开头 – novak 2010-07-14 16:41:08

1

根据Python的文档file objects,你所做的迭代不应该特别慢,并且搜索子串也应该是很好的速度方式。

我看不出有什么理由说明你的代码应该很慢,所以如果你需要它快一点,你可能不得不用C语言重写它,并使用mmap()来快速访问源文件。

+1

我想知道如何搜索我一次不想要的行。 Lke一次搜索全部30,000个。 – novak 2010-07-14 16:51:23

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块,将其分割成行和搜索,但不要忘记每100MB间隔加入一行(如果是这种情况,我可以详细说明)

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) 
+0

这些都不会有帮助,对不起。 #1和#2没有意义,因为没有存储输入或输出行的好处 - 这是一个流,您可以保留或拒绝一行,并且您不需要以前或以下来做出决定,你也不需要对其进行分类。 #3将减缓处理,因为人工添加lambda fn invocations每行*和*构建结果列表 – 2010-07-14 20:23:49

+0

@Nas Banov,你有计时吗?我会说尝试在内存中加载一个文件并逐行阅读,你会看到很多不同之处。 – 2010-07-15 07:10:25

+0

欢迎您出示支持您索赔的数据 – 2010-07-15 08:24:01

0

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

如果您想要包含某个字符串的所有行,或者更一般地包含任何给定的字符串集合或正则表达式,请使用grep。对于大型数据集可能会明显更快。

0

这让我想起Tim Bray描述的一个问题,他试图使用多核机器从Web服务器日志文件中提取数据。结果在The Wide Finder ProjectWide Finder 2中描述。所以,如果串行优化对你来说速度不够快,这可能是一个开始的地方。在许多语言中都有这种问题的例子,including python。从最后一个环节的关键报价:

摘要

在这篇文章中,我们采取了一个比较快的Python实现和优化它,使用 一些技巧:

  • 预编译RE图案
  • 候选线的快速过滤
  • 块状读数
  • 多PLE处理
  • 存储器映射,与RE操作的支持组合上映射缓冲器

这降低到从6.7秒解析日志数据的200兆字节到0.8秒 在测试机上所需的时间。或者换句话说,最终版本比原始Python版本快了8倍以上,并且(可能)比Tim的原始版本 Erlang版本快了600倍。

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

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保证以新行结束块。为了识别(迭代器明智)的针串的所有出现一个草垛字符串中:

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语句合并为一个,只是为了减少嵌套。

注意:这段代码没有经过测试,所以可能会出现(希望是小的;-)错误,例如一个一个的。性能需要调整块大小,并且必须通过对特定机器和数据进行测量来校准。你的旅费可能会改变。法律禁止的地方无效。

+0

Python I/O实现和/或底层C库不会读取引擎盖下的大块吗?我认为在Python中显式执行会更慢,尽管我承认我没有对它进行基准测试。 – 2010-07-14 17:51:49

+0

请注意所描述的问题是不同的 - 他需要根据〜30000 *不同*标准过滤文件,而不是单个'S0414'。这改变了游戏 – 2010-07-14 18:42:45

+0

@Daniel,你可以指定缓冲区,但是每个缓冲区仍然会被分解成其中的所有行,其中大部分都是不相关的;我试图节省这部分成本 - 只隔离相关线路。当然,如果只有一小部分线路是相关的,这只会节省时间。 – 2010-07-14 23:04:21

0

加速它的最好方法是如果特定字符串S0414总是出现在相同的字符位置,所以不必每行都做出几次失败的比较(你说他们以不同的名字开头),它可以做一个完成。

例如如果您的文件有像

GLY S0414 GCT 
ASP S0435 AGG 
LEU S0432 CCT 

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

2

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

+0

我觉得你的回答很有意思,但请注意,这对OP来说似乎是适得其反的。顺便说一句,不要正则表达式函数 - 无论是Python或C库 - 已经包含优化算法(说Boyer-Moore)? – 2010-07-14 20:34:10

+1

@Nas:虽然我承认自己实现这个算法是一个高级主题,但我链接到一个相对友好的库,应该可以由新手使用。顺便说一下,我不会假定正则表达式有这样的优化,因为这种数据类型(即Tries)专门针对具有许多同时搜索查询的搜索进行优化。我希望用状态机来实现一个正则表达式,这可能不会像'''.join(keywords)'这样的策略做得很好......但显然更好的策略是测试解决方案和看哪个更快。 – Brian 2010-07-14 21:30:31

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) 

根据什么的首个字母的那些目标的分布可以通过一个显著量削减你比较下来。如果你不知道值的出现位置,那么你正在谈论一个长期操作,因为你必须比较数十万个 - 比如说30万到30,000次。这是900万的比较,这将需要一个时间。

+0

嗯,我怀疑使用简单的'set()'而不是这个自制的first-leter词典列表将会更快。 – 2010-07-14 20:29:17