2011-07-24 91 views
5

我有一个项目,我给了一个文件,我需要从文件中提取字符串。基本上想到在Linux中的“字符串”命令,但我在python中这样做。下一个条件是文件以流(例如字符串)的形式提供给我,所以使用其中一个子进程函数运行字符串的明显答案也不是一个选项。从Python中的二进制文件中提取字符串

我写了这个代码:

def isStringChar(ch): 
    if ord(ch) >= ord('a') and ord(ch) <= ord('z'): return True 
    if ord(ch) >= ord('A') and ord(ch) <= ord('Z'): return True 
    if ord(ch) >= ord('0') and ord(ch) <= ord('9'): return True 

    if ch in ['/', '-', ':', '.', ',', '_', '$', '%', '\'', '(', ')', '[', ']', '<', '>', ' ']: return True 

# default out 
return False 

def process(stream): 
dwStreamLen = len(stream) 
if dwStreamLen < 4: return None 

dwIndex = 0; 
strString = '' 
for ch in stream: 
    if isStringChar(ch) == False: 
     if len(strString) > 4: 
      #print strString 
      strString = '' 
    else: 
     strString += ch 

这种技术上的工作,但WAY缓慢。例如,我能够在500Meg可执行文件上使用strings命令,并在不到1秒的时间内生成了价值300K的字符串。我通过上面的代码运行了相同的文件,花了16分钟。

在那里有一个库,可以让我在没有python延迟的情况下执行此操作吗?

谢谢!

+0

如果您可以阅读C [GNU字符串的源代码](http://sourceware.org /cgi-bin/cvsweb.cgi/src/binutils/strings.c?rev=1.48&content-type=text/x-cvsweb-markup&cvsroot=src)可能会有帮助。它只有几百行,所以没那么糟糕。 –

回答

7

与David Wolever's相似的速度,使用re,Python的正则表达式库。优化的一个小故事是,你写的代码越少,速度就越快。循环的库函数通常以C语言实现,并且速度比您希望的要快。 char in set()比检查自己快。 Python在这方面与C相反。

import sys 
import re 

chars = r"A-Za-z0-9/\-:.,_$%'()[\]<> " 
shortest_run = 4 

regexp = '[%s]{%d,}' % (chars, shortest_run) 
pattern = re.compile(regexp) 

def process(stream): 
    data = stream.read() 
    return pattern.findall(data) 

if __name__ == "__main__": 
    for found_str in process(sys.stdin): 
     print found_str 

在4K的块工作计划很巧妙,但在边缘案件棘手一点与re。 (其中两个字符位于4k块的末尾,而下一个2位于下一个块的开头)

+0

不错的建议。我同意 - 对于较小的流(即,可以适应内存),这当然是可取的。我敢打赌,你甚至可以把这个流块分成几块,然后把这个正则表达式在不可打印的字符上分割出来,然后运行这个正则表达式......嗯...... –

+0

@dougallj:这是非常快的。谢谢!现在,如果你可以让它找到unicode字符串,我会给你买一瓶啤酒;-)我不会有(并且显然没有)想过使用re。我能够在33秒内处理我的500Meg测试文件。这完全在我的设计规格范围内。 – tjac

+0

@tjac:什么是unicode字符串? –

5

至少有一个问题是你正在读整个流到内存中(… = len(stream)),另一个是你的isStringChar函数非常慢(函数调用相对较慢,而且你做了很多他们)。

更好的方式是这样的:

import sys 
import string 

printable = set(string.printable) 

def process(stream): 
    found_str = "" 
    while True: 
     data = stream.read(1024*4) 
     if not data: 
      break 
     for char in data: 
      if char in printable: 
       found_str += char 
      elif len(found_str) >= 4: 
       yield found_str 
       found_str = "" 
      else: 
       found_str = "" 

if __name__ == "__main__": 
    for found_str in process(sys.stdin): 
     print found_str 

这将是更快,因为:

  • 的“是字符打印的”查找与一个查找执行(和O(1)操作)直接调用(如果我没有弄错)到C函数(这将是非常快)。
  • 流以4k块处理,这将改善大型输入的内存使用和运行时,因为不需要交换。
+2

您需要一个'else:found_str =“”'。代码当前会打印它所绊倒的任何4个可打印字符,而不管它们是否连续。 – dougallj

+0

D'oh。谢谢。现在修复。 (如果这并不明显,我实际上没有*测试*此代码...) –

相关问题