5

ファイル内のトークン':path'をシークしようとしています。次に、次のすべての(任意の桁数)数値を数値として読み取ります(したがって、':path、123'の場合、ファイル内の、をシークしてから、整数123)。次に、現在のシーク位置とpos + 123の間の文字を読み取ります(リストなどに保存します)。次に、「:path」の次の一致までシークし、プロセスを繰り返します。

私は少し次のような関数が欲しいです:

def fregseek(FILE、current_seek、/ regex /):

。
。
  value_found =?#:path、[0-9]+の次のN文字を読み取った結果
。
。
  next_start_seek、value_foundを返します

1行に「:path」と一致するものはいくつでもあり、その文字列は「、」の後に指定された文字数の範囲内で発生する可能性があります。私は、各行を読み取る厄介なゴミの束を作成しました。次に、各行について、一致によって示される最初のN文字を切り刻み、すべてが食べ尽くされるまで文字列の処理を続けます。次に、次の文字列を読み取ります。

これはひどいです、私が本当にする必要があるのはシークだけであるときに潜在的に巨大なファイルからすべての行を丸呑みする必要はありません(特に改行は無関係なので、行が簡単であるという理由だけで余分な処理ステップがありますファイルからのプルはばかげています)。

ですから、それが私の問題です。一致するものを探し、値を読み取り、その値の最後から次の一致を探すなど、ファイルがなくなるまで続ける必要があります。

誰かがこれで私を助けることができるならば、私は彼らから聞いてうれしいです:)

可能であれば非標準ライブラリを避けたいです。最短のコードも必要ですが、これは私の懸念事項の中で最も少ないものです(速度とメモリ消費は重要な要素ですが、一部をブートストラップするためだけに50loc余分にしたくありません。小さな関数が入っているライブラリは、それが何であるかを知っていれば、ただ引き裂くことができます)。

私はPythonコードを好みますが、この点でperlがpythonに勝る場合は、代わりにperlを使用します。ひどく遅くならない限り、賢いsed / awk/bashスクリプトなども使用できます。

よろしくお願いします。

4

2 に答える 2

3

Pythonではほぼ1行でそれを行うことができます:

with open('filename.txt') as f:
    text = f.read()

results = [text[i[0]:i[0] + i[1]] for i in 
           ((m.end(), int(m.group(1))) for m in
            re.finditer(':path,([0-9]+)', text))]

注:テストされていません...

于 2012-09-26T21:55:58.727 に答える
3

正規表現が必要ない場合は、検索してスライスするだけでこれを行うことができます。

いずれにせよ、簡単な解決策は、ファイル全体をメモリに読み込み、結果のstr/bytesオブジェクトを見つけてスライスすることです。

ただし、ファイル全体をメモリに読み込むことができない(または読みたくない)場合は、これは機能しません。

幸い、ファイルが<< 2GBであるという事実を信頼できる場合、または64ビットPythonでのみ作業する必要があり、妥当なプラットフォーム(POSIX、最新のWindowsなど)を使用している場合はmmap、ファイルを作成できます。代わりにメモリに。オブジェクトには文字列と同じメソッドのmmapサブセットがあるため、ファイル全体をメモリに読み込んだかのように、文字列を持っているふりをすることができますが、Pythonの実装とOSを頼りにそれを作成することができます合理的な効率で作業します。

Pythonのバージョンによってはre、mmapを文字列のようにスキャンできない場合や、動作するが遅い場合、または正常に動作する場合があります。したがって、最初にそれを試してみた方がよいでしょう。例外がスローされないか、予想よりもはるかに遅くならない場合は、これで完了です。

def findpaths(fname):
    with open(fname, 'rb') as f:
        m = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
        for match in re.finditer(':path,([0-9]+)', m):
            yield m[match.end():match.end()+int(match.group(1))]

(これはBrtHの答えと同じですが、文字列の代わりにmmapを使用し、リストの代わりにジェネレーターに再構築されています。ただし、もちろん、角かっこを括弧に置き換えるだけで後者の部分を実行できます。)

(効率的に)できない古い(またはCPython以外の?)バージョンのPythonを使用している場合はremmapもう少し複雑になります。

def nextdigits(s, start):
  return ''.join(itertools.takewhile(str.isdigit,
                                     itertools.islice(s, start, None)))

def findpaths(fname):
  with open(fname, 'rb') as f:
    m = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
    i = 0
    while True:
      n = m.find(':path', i)
      if n == -1: return
      countstr = nextdigits(m, n+6)
      count = int(countstr)
      n += 6 + len(countstr)
      yield m[n:n+count]
      i = n + 6 + count

これはおそらくnextdigits関数を書くための最速の方法ではありません。それが実際に問題になるかどうかはわかりませんが(時間を計って確認してください)、問題がある場合は、スライスしm[n+6:n+A_BIG_ENOUGH_NUMBER]て正規表現するか、カスタムループを作成するか、または…一方、それがボトルネックである場合は、 JIT(PyPy、Jython、またはIronPython)を使用するインタープリターに切り替えることで、はるかに多くのメリットが得られる可能性があります…</ p>

私のテストでは、物事を分割しfindpathsます。文字列のようなオブジェクトを取得し、呼び出し元がwith openandmmapビットを実行mしてfindpaths;に渡すだけです。私はここで簡潔にするためにそれをしませんでした。

とにかく、私は次のデータに対して両方のバージョンをテストしました:

BLAH:path,3abcBLAH:path,10abcdefghijklmnBLAH:path,3abc:path,0:path,3abc

そして、出力は次のとおりです。

abc
abcdefghij
abc

abc

正しいと思いますか?

i以前のバージョンで100%CPUでスピンした場合、ループ内で適切にインクリメントしなかったと推測されます。これが、タイトな解析ループでその動作が発生する最も一般的な理由です。とにかく、現在のバージョンで再現できる場合は、データを投稿してください。

于 2012-09-26T23:08:58.277 に答える