6

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

私はこのコードを書きました:

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

これは技術的には機能しますが、かなり遅いです。たとえば、strings コマンドを 500Meg 実行可能ファイルで使用することができ、1 秒未満で 300k 相当の文字列を生成しました。上記のコードで同じファイルを実行したところ、16 分かかりました。

Python の遅延の負担なしでこれを実行できるライブラリはありますか?

ありがとう!

4

2 に答える 2

9

rePython の正規表現ライブラリを使用して、David Wolever のものと同様の速度です。最適化の簡単な話は、記述するコードが少ないほど高速になるということです。ループするライブラリ関数は 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. (2 文字は 4k ブロックの終わりにあり、次の 2 文字は次のブロックの先頭にあります)

于 2011-07-24T03:27:40.437 に答える
5

あなたの問題の少なくとも 1 つは、ストリーム全体をメモリに読み込んでいることです ( … = len(stream))。もう 1 つは、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

次の理由により、これははるかに高速になります。

  • 「is character printable」ルックアップは、(私が間違っていなければ) C 関数 (非常に高速) を直接呼び出す 1 つのセット ルックアップ (および O(1) 操作) で実行されます。
  • ストリームは 4k チャンクで処理されます。これにより、スワッピングが不要になるため、大量の入力でのメモリ使用とランタイムが改善されます。
于 2011-07-24T02:59:46.987 に答える