2011-07-24 22 views
5

私はファイルを与えられたプロジェクトを持っています。ファイルから文字列を抽出する必要があります。基本的にはlinuxの "strings"コマンドを考えるが、私はこれをPythonでやっている。次の条件は、ファイルがストリーム(例えば文字列)として私に与えられているので、サブプロセス関数の1つを使って文字列を実行するという明白な答えはオプションでもありません。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

[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と同様の速度で、Pythonの正規表現ライブラリreを使用します。最適化の短い話は、書くコードが少ないほど速くなるということです。ループするライブラリ関数は、C言語で実装されることが多く、期待するよりも速くなります。 char in set()が自分よりも速いのは同じです。 Pythonはその点でCの反対です。

4Kチャンクでの作業
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 

は巧妙であるが、reとエッジの場合にビットトリッキーであるだろう。 (2文字は4kブロックの最後にあり、次の2文字は次のブロックの先頭にあります)

+0

素敵な提案。私は同意します - 小さなストリーム(つまり、メモリに収まる)の場合、これは確かに望ましいことです。私はあなたもストリームをチャンクすることができ、次に、この正規表現を印刷できない文字に分割して実行することができると考えています...うーん... –

+0

@dougallj:これは邪悪な速さです。ありがとう!今でもユニコード文字列を見つけることができれば、私はあなたにビールを購入します;-)私は持っていないでしょう。私は500秒のテストファイルを33秒で処理することができました。それは私の設計仕様限界の範囲内です。 – tjac

+0

@tjac:ユニコード文字列とは何ですか? –

5

少なくとも1つの問題は、ストリーム全体をメモリ(… = len(stream))に読み込んでいることと、isStringCharの機能が非常に遅い(関数呼び出しが比較的遅く、それら)。ルック「の文字印刷可能であるが、」1セットの検索(およびO(1)を用いて行われる

  • :これは、はるかに高速になります

    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 
    

    ベターこのようなものになるだろう(私が間違っていない場合)C関数(これは非常に速くなります)に直接呼び出します。

  • ストリームは4kチャンクで処理されるため、スワッピングが不要なため、メモリ使用量と実行時間が大幅に短縮されます。
+2

あなたは 'else:found_str =" "'が必要です。このコードでは、現在行があるかどうかにかかわらず、手を加えた4つの印刷可能な文字をすべて印刷しています。 – dougallj

+0

D'oh。ありがとう。今修正されました。 (それが明らかにしなかったなら、私は実際に*このコードをテストしていません...) –

関連する問題