簡體   English   中英

python:在string中查找第一個字符串

[英]python: find first string in string

給定一個字符串和一個子字符串列表,我想要第一個位置,任何子字符串都出現在字符串中。 如果沒有子字符串出現,則返回0.我想忽略大小寫。

有什么比pythonic更pythonic:

given = 'Iamfoothegreat'
targets = ['foo', 'bar', 'grea', 'other']
res = len(given)
for t in targets:
    i = given.lower().find(t)
    if i > -1 and i < res:
        res = i

if res == len(given):
    result = 0
else:
    result = res

該代碼有效,但似乎效率低下。

我不會返回0因為可能是起始索引,要么使用-1,無或其他一些不可能的值,您可以簡單地使用try / except並返回索引:

def get_ind(s, targ):
    s = s.lower() 
    for t in targets:
        try:            
            return s.index(t.lower())
        except ValueError:
            pass
    return None # -1, False ...

如果你想忽略輸入字符串的大小寫,那么在循環之前設置s = s.lower()

你也可以這樣做:

def get_ind_next(s, targ):
   s = s.lower() 
   return next((s.index(t) for t in map(str.lower,targ) if t in s), None)

但是,對於每個子字符串而言,最糟糕的是兩次查找,而不是使用try / except。 它至少也會在第一場比賽中短路。

如果你真的想要所有的min,那么改為:

def get_ind(s, targ):
    s = s.lower()
    mn = float("inf")
    for t in targ:
        try:
            i = s.index(t.lower()) 
            if i < mn:
                mn = i 
        except ValueError:
            pass
    return mn   

def get_ind_next(s, targ):
   s = s.lower()
   return min((s.index(t) for t in map(str.lower, targ) if t in s), default=None)

default=None僅適用於python> = 3.4,因此如果您使用的是python2,那么您將不得不稍微更改邏輯。

Timings python3:

In [29]: s = "hello world" * 5000
In [30]:  s += "grea" + s
In [25]: %%timeit
   ....: targ = [re.escape(x) for x in targets]
   ....: pattern = r"%(pattern)s" % {'pattern' : "|".join(targ)}
   ....: firstMatch = next(re.finditer(pattern, s, re.IGNORECASE),None)
   ....: if firstMatch:
   ....:     pass
   ....: 
100 loops, best of 3: 5.11 ms per loop
In [18]: timeit get_ind_next(s, targets)
1000 loops, best of 3: 691 µs per loop

In [19]: timeit get_ind(s, targets)
1000 loops, best of 3: 627 µs per loop

In [20]:  timeit  min([s.lower().find(x.lower()) for x in targets if x.lower() in s.lower()] or [0])
1000 loops, best of 3: 1.03 ms per loop

In [21]: s = 'Iamfoothegreat'
In [22]: targets = ['bar', 'grea', 'other','foo']
In [23]: get_ind_next(s, targets) == get_ind(s, targets) ==  min([s.lower().find(x.lower()) for x in targets if x.lower() in s.lower()] or [0])
Out[24]: True

Python2:

In [13]: s = "hello world" * 5000
In [14]:  s += "grea" + s

In [15]: targets = ['foo', 'bar', 'grea', 'other']
In [16]: timeit get_ind(s, targets)1000 loops, 
best of 3: 322 µs per loop

In [17]:  timeit  min([s.lower().find(x.lower()) for x in targets if x.lower() in s.lower()] or [0])
1000 loops, best of 3: 710 µs per loop

In [18]: get_ind(s, targets) ==  min([s.lower().find(x.lower()) for x in targets if x.lower() in s.lower()] or [0])
Out[18]: True

你也可以將第一個與min結合起來:

def get_ind(s, targ):
    s,mn = s.lower(), None
    for t in targ:
        try:
            mn = s.index(t.lower())
            yield mn
        except ValueError:
            pass
    yield mn

哪個做同樣的工作,它只是更好一點,也許稍快一點:

In [45]: min(get_ind(s, targets))
Out[45]: 55000

In [46]: timeit min(get_ind(s, targets))
1000 loops, best of 3: 317 µs per loop

使用正則表達式

另一個例子就是使用正則表達式,因為認為python正則表達式實現速度非常快。 不是我的正則表達式功能

import re

given = 'IamFoothegreat'
targets = ['foo', 'bar', 'grea', 'other']

targets = [re.escape(x) for x in targets]    
pattern = r"%(pattern)s" % {'pattern' : "|".join(targets)}
firstMatch = next(re.finditer(pattern, given, re.IGNORECASE),None)
if firstMatch:
    print firstMatch.start()
    print firstMatch.group()

輸出是

3
foo

如果什么也沒發現輸出什么都沒有。 應該自我解釋,以檢查是否找不到任何東西。

更常見的不是pythonic

也給你匹配的字符串

given = 'Iamfoothegreat'.lower()
targets = ['foo', 'bar', 'grea', 'other']

dct = {'pos' : - 1, 'string' : None};
given = given.lower()

for t in targets:
    i = given.find(t)
    if i > -1 and (i < list['pos'] or list['pos'] == -1):
        dct['pos'] = i;
        dct['string'] = t;

print dct

輸出是:

{'pos': 3, 'string': 'foo'}

如果未找到元素:

{'pos': -1, 'string': None}

兩者的表現比較

用這個字符串和模式

given = "hello world" * 5000
given += "grea" + given
targets = ['foo', 'bar', 'grea', 'other']

帶有timeit的1000個循環:

regex approach: 4.08629107475 sec for 1000
normal approach: 1.80048894882 sec for 1000

10個循環。 現在有更大的目標(目標* 1000):

normal approach: 4.06895017624 for 10
regex approach: 34.8153910637 for 10

您可以使用以下內容:

answer = min([given.lower().find(x.lower()) for x in targets 
    if x.lower() in given.lower()] or [0])

演示1

given = 'Iamfoothegreat'
targets = ['foo', 'bar', 'grea', 'other']

answer = min([given.lower().find(x.lower()) for x in targets 
    if x.lower() in given.lower()] or [0])
print(answer)

產量

3

演示2

given = 'this is a different string'
targets = ['foo', 'bar', 'grea', 'other']

answer = min([given.lower().find(x.lower()) for x in targets 
    if x.lower() in given.lower()] or [0])
print(answer)

產量

0

我也認為以下解決方案非常易讀:

given = 'the string'
targets = ('foo', 'bar', 'grea', 'other')

given = given.lower()

for i in range(len(given)):
    if given.startswith(targets, i):
        print i
        break
else:
    print -1

您的代碼相當不錯,但是您可以通過將.lower轉換移出循環來使其更有效:不需要為每個目標子字符串重復它。 使用列表推導可以稍微壓縮代碼,但這並不一定會使代碼更快。 我使用嵌套列表comp來避免為每個t調用given.find(t)兩次。

我已將代碼包裝在函數中以便於測試。

def min_match(given, targets):
    given = given.lower()
    a = [i for i in [given.find(t) for t in targets] if i > -1]
    return min(a) if a else None

targets = ['foo', 'bar', 'grea', 'othe']

data = (
    'Iamfoothegreat', 
    'IAMFOOTHEGREAT', 
    'Iamfothgrease',
    'Iamfothgret',
)

for given in data:
    print(given, min_match(given, targets))    

產量

Iamfoothegreat 3
IAMFOOTHEGREAT 3
Iamfothgrease 7
Iamfothgret None

嘗試這個:

def getFirst(given,targets):
    try:
        return min([i for x in targets for i in [given.find(x)] if not i == -1])
    except ValueError:
        return 0

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM