我有一个项目,我给了一个文件,我需要从文件中提取字符串。基本上认为linux中的"字符串"命令,但我在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
这在技术上是有效的,但速度很慢。例如,我能够在一个500Meg的可执行文件上使用strings命令,它在不到1秒的时间内生成了价值300k的字符串。我通过上面的代码运行相同的文件,花了16分钟。
是否有一个库可以让我在没有python延迟负担的情况下做到这一点?
谢谢!
与David Wolever的速度相似,使用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块的末尾,后面两个字符位于下一个块的开始)
至少你的一个问题是你正在读取整个流到内存(… = 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块处理,这将改善大输入时的内存使用和运行时间,因为不需要交换。