2

比較しようとしているファイル名がいくつかあります。ここではいくつかの例を示します。

files = ['FilePrefix10.jpg', 'FilePrefix11.jpg', 'FilePrefix21.jpg', 'FilePrefixOoufhgonstdobgfohj#lwghkoph[]**^.jpg']

私がする必要があるのは、各ファイル名から「FilePrefix」を抽出することです。これは、ディレクトリによって異なります。多くのjpgを含むいくつかのフォルダーがあります。各フォルダー内で、各 jpg には、そのディレクトリ内の他のすべての jpg と共通の FilePrefix があります。jpg ファイル名の変数部分が必要です。FilePrefix がどのようなものになるかを事前に予測することはできません。

difflib (Python) を使用して 2 つのファイル名を比較し、そのように FilePrefix (およびその後の変数部分) を抽出するというアイデアがありました。次の問題に遭遇しました。

>>>> comp1 = SequenceMatcher(None, files[0], files[1])
>>>> comp1.get_matching_blocks()
[Match(a=0, b=0, size=11), Match(a=12, b=12, size=4), Match(a=16, b=16, size=0)]

>>>> comp1 = SequenceMatcher(None, files[1], files[2])
>>>> comp1.get_matching_blocks()
[Match(a=0, b=0, size=10), Match(a=11, b=11, size=5), Match(a=16, b=16, size=0)]

ご覧のとおり、最初のsizeものは一致しません。10 の位と 1 位の位が混同されているため、2 つ以上のファイルの違いを照合するのが難しくなっています。sizeディレクトリ内のすべてのファイルから最小値を見つける正しい方法はありますか? または、FilePrefix を抽出するより良い方法はありますか?

ありがとうございました。

4

2 に答える 2

2

これは「十の位と数字の位を混同している」ということではなく、最初の対戦では十の位が変わらないため、一致する接頭辞の一部と見なされます。

あなたのユースケースでは、このあいまいさに対する非常に簡単な解決策があるようです。隣接するすべてのペアを一致させ、最小値を取るだけです。このような:

def prefix(x, y):
    comp = SequenceMatcher(None, x, y)
    matches = comp.get_matching_blocks()
    prefix_match = matches[0]
    prefix_size = prefix_match[2]
    return prefix_size

pairs = zip(files, files[1:])
matches = (prefix(x, y) for x, y in pairs)
prefixlen = min(matches)
prefix = files[0][:prefixlen]

このprefix関数は非常に単純ですが、1 つのことを除いて、map. 2.7には、 の 2 回目の呼び出しがではなく を返すという厄介なバグがあるため、[2]代わりにを使用しました。これはそのままのコードには影響しませんが、デバッグを追加すると壊れます。.sizedifflibget_matching_blockstuplenamedtupleprint

現在、とをpingすることpairsによって作成された、すべての隣接する名前のペアのリストです。(これが明確でない場合は、 .Python 3.x を使用している場合は、印刷可能なリストではなく遅延イテレータを返すため、代わりに使用する必要があります。)zipnamesnames[1:]print(zip(names, names[1:])print(list(zip(names, names[1:]))zip

次に、それぞれのペアを呼び出しprefixて、返された最小値を取得します。それminがそのためです。(ジェネレーター式を渡していますが、これは最初はトリッキーな概念になる可能性がありますが、リストを構築しないリスト内包表記と考えれば、非常に簡単です。)

読みやすいままにしながら、明らかにこれを 2 ~ 3 行に圧縮できます。

prefixlen = min(SequenceMatcher(None, x, y).get_matching_blocks()[0][2] 
                for x, y in zip(files, files[1:]))
prefix = files[0][:prefixlen]

SequenceMatcherただし、ここではおそらくやり過ぎだと考える価値があります。最長のプレフィックスの一致だけでなく、どこでも最長の一致を探しています。つまり、文字列の長さは本質的に O(N^3) であり、M は結果の長さである O(NM) である必要があるだけです。 . さらに、たとえば、最長の接頭辞よりも長い接尾辞が存在することは考えられないわけではないため、間違った結果が返されます。

では、なぜそれを手動でやらないのですか?

def prefixes(name):
    while name:
        yield name
        name = name[:-1]

def maxprefix(names):
    first, names = names[0], names[1:]
    for prefix in prefixes(first):
        if all(name.startswith(prefix) for name in names):
            return prefix

prefixes(first)'FilePrefix10.j 'F'`'FilePrefix10.jpg'を与えるだけです。したがって、それらをループして、それぞれが他のすべての名前のプレフィックスでもあるかどうかを確認し、最初のものを返します。'FilePrefix10.jp',, etc. down to


そして、接頭辞ごとではなく文字ごとに考えると、これをさらに速く行うことができます。

def maxprefix(names):
    for i, letters in enumerate(zip(*names)):
        if len(set(letters)) > 1:
            return names[0][:i]

ここでは、最初の文字がすべての名前で同じかどうか、次に 2 番目の文字がすべての名前で同じかどうか、というようにチェックしているだけです。それが失敗するものを見つけたら、プレフィックスはそれまでのすべての文字です (任意の名前から)。

zip、名前のリストをタプルのリストに再編成します。最初のリストは各名前の最初の文字、2 番目は各名前の 2 番目の文字、というようになります。つまり、[('F', 'F', 'F', 'F'), ('i', 'i', 'i', 'i'), …].

enumerate、値とともにインデックスを提供するだけです。したがって、取得する代わりに取得し('F', 'F', 'F', 'F')ます0, ('F, 'F', F', 'F')。最後のステップでそのインデックスが必要です。

さて、('F', 'F', 'F', 'F')それらがすべて同じであることを確認するために、それらをset. それらがすべて同じである場合、セットには <code>{'F'}, then{'i'}などの 1 つの要素しかありません。そうでない場合は、複数の要素があります: <code>{'1', '2'}—これが、接頭辞を過ぎたことを知る方法です。

于 2013-12-19T23:49:55.483 に答える
1

確実にする唯一の方法は、すべてのファイル名をチェックすることです。したがって、それらすべてを繰り返し処理し、保持されている最大一致文字列をチェックします。

次のようなことを試してみてください:

files = ['FilePrefix10.jpg',
         'FilePrefix11.jpg',
         'FilePrefix21.jpg',
         'FilePrefixOoufhgonstdobgfohj#lwghkoph[]**^.jpg',
         'FileProtector354.jpg
         ]
prefix=files[0]
max = 0
for f in files:
    for c in range(0, len(prefix)):
        if prefix[:c] != f[:c]:
            prefix = f[:c-1]
            max = c - 1
print prefix, max

ソリューションの「非 Pythonicness」をご容赦ください。ただし、アルゴリズムがどのレベルのプログラマーにも明らかであるようにしたかったのです。

于 2013-12-19T23:55:56.547 に答える