web-dev-qa-db-ja.com

文字列がリストの可変長プレフィックスの1つで始まるかどうかを見つける

名前がリストのプレフィックスのいずれかで始まるかどうかを確認してから、次のように削除する必要があります。

_if name[:2] in ["i_", "c_", "m_", "l_", "d_", "t_", "e_", "b_"]:
    name = name[2:]
_

上記は、長さが2のリスト接頭辞に対してのみ機能します。 可変長のプレフィックスにも同じ機能が必要です。

それはどのように効率的に行われますか(小さなコードと優れたパフォーマンス)?

Forループが各プレフィックスを反復処理し、name.startswith(prefix)をチェックして、最後にプレフィックスの長さに従って名前をスライスしますが、これは多くのコードであり、おそらく非効率的で、「Python以外」です。

誰かが素晴らしい解決策を持っていますか?

21
Kawu

少し読みにくいですが、これはうまくいきます:

name=name[len(filter(name.startswith,prefixes+[''])[0]):]
13
Vaughn Cato

str.startswith(prefix [、start [、end]])¶

文字列がプレフィックスで始まる場合はTrueを返し、それ以外の場合はFalseを返します。 prefixは、検索する接頭辞のタプルにすることもできます。オプションの開始では、その位置で始まるテスト文字列。オプションの終了で、その位置で文字列の比較を停止します。

$ ipython
Python 3.5.2 (default, Nov 23 2017, 16:37:01)
Type 'copyright', 'credits' or 'license' for more information
IPython 6.4.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: prefixes = ("i_", "c_", "m_", "l_", "d_", "t_", "e_", "b_")

In [2]: 'test'.startswith(prefixes)
Out[2]: False

In [3]: 'i_'.startswith(prefixes)
Out[3]: True

In [4]: 'd_a'.startswith(prefixes)
Out[4]: True
40
dm03514
for prefix in prefixes:
    if name.startswith(prefix):
        name=name[len(prefix):]
        break
5
unutbu

正規表現はおそらくあなたに最高の速度を与えます:

prefixes = ["i_", "c_", "m_", "l_", "d_", "t_", "e_", "b_", "also_longer_"]
re_prefixes = "|".join(re.escape(p) for p in prefixes)

m = re.match(re_prefixes, my_string)
if m:
    my_string = my_string[m.end()-m.start():]
3
Ned Batchelder

アンダースコアの前の文字になるようにプレフィックスを定義すると、次のことを確認できます

if name.partition("_")[0] in ["i", "c", "m", "l", "d", "t", "e", "b", "foo"] and name.partition("_")[1] == "_":
    name = name.partition("_")[2]
2
Foo Bah

正規表現、テスト済み:

import re

def make_multi_prefix_matcher(prefixes):
    regex_text = "|".join(re.escape(p) for p in prefixes)
    print repr(regex_text)
    return re.compile(regex_text).match

pfxs = "x ya foobar foo a|b z.".split()
names = "xenon yadda yeti food foob foobarre foo a|b a b z.yx zebra".split()

matcher = make_multi_prefix_matcher(pfxs)
for name in names:
    m = matcher(name)
    if not m:
        print repr(name), "no match"
        continue
    n = m.end()
    print repr(name), n, repr(name[n:])

出力:

'x|ya|foobar|foo|a\\|b|z\\.'
'xenon' 1 'enon'
'yadda' 2 'dda'
'yeti' no match
'food' 3 'd'
'foob' 3 'b'
'foobarre' 6 're'
'foo' 3 ''
'a|b' 3 ''
'a' no match
'b' no match
'z.yx' 2 'yx'
'zebra' no match
2
John Machin

filterの使用についてはどうですか?

prefs = ["i_", "c_", "m_", "l_", "d_", "t_", "e_", "b_"]
name = list(filter(lambda item: not any(item.startswith(prefix) for prefix in prefs), name))

各リスト項目と接頭辞の比較は、最初の一致で効率的に停止することに注意してください。この動作は、any値が見つかるとすぐに戻るTrue関数によって保証されます。例:

def gen():
    print("yielding False")
    yield False
    print("yielding True")
    yield True
    print("yielding False again")
    yield False

>>> any(gen()) # last two lines of gen() are not performed
yielding False
yielding True
True

または、re.matchの代わりにstartswith

import re
patt = '|'.join(["i_", "c_", "m_", "l_", "d_", "t_", "e_", "b_"])
name = list(filter(lambda item: not re.match(patt, item), name))
2
etuardu

検索と効率に関しては、常にアルゴリズムを改善するためのインデックス付け技術を考えます。プレフィックスの長いリストがある場合は、プレフィックスの最初の文字をdictに単純にインデックス付けすることで、メモリ内インデックスを使用できます。

このソリューションは、プレフィックスの長いリストがあり、パフォーマンスが問題になる場合にのみ価値があります。

pref = ["i_", "c_", "m_", "l_", "d_", "t_", "e_", "b_"]

#indexing prefixes in a dict. Do this only once.
d = dict()
for x in pref:
        if not x[0] in d:
                d[x[0]] = list()
        d[x[0]].append(x)


name = "c_abcdf"

#lookup in d to only check elements with the same first character.
result = filter(lambda x: name.startswith(x),\
                        [] if name[0] not in d else d[name[0]])
print result
1

これにより、オンザフライでリストが編集され、接頭辞が削除されます。 breakは、特定のアイテムに対してプレフィックスが見つかると、残りのプレフィックスをスキップします。

items = ['this', 'that', 'i_blah', 'joe_cool', 'what_this']
prefixes = ['i_', 'c_', 'a_', 'joe_', 'mark_']

for i,item in enumerate(items):
    for p in prefixes:
        if item.startswith(p):
            items[i] = item[len(p):]
            break

print items

出力

['this', 'that', 'blah', 'cool', 'what_this']
0
Mark Tolonen

単純な正規表現を使用できます。

import re
prefixes = ("i_", "c_", "longer_")
re.sub(r'^(%s)' % '|'.join(prefixes), '', name)

または、アンダースコアの前にあるものが有効なプレフィックスである場合:

name.split('_', 1)[-1]

これにより、最初のアンダースコアの前の任意の数の文字が削除されます。

0
wihlke