如何获取用特殊字符填充的子字符串的索引

时间:2013-11-19 15:49:54

标签: python

我正在寻找一个oneliner来获取字符串中第一个和最后一个非连字符的索引。在以下示例中,索引为ad。我会删除任何前导或尾随连字符-

我必须做几十亿次这个操作。

---abc--d--
ans: 3, 8

---abc----
ans: 3,5

根据我从一些最好的编码器那里得到的回复,我试图找到最有效的代码。运行时间如下:

In [48]: s = 'a-b-c-d'

In [49]: %timeit next(re.finditer('[^-].*[^-]', s)).span()
100000 loops, best of 3: 3.05 us per loop

In [50]: %timeit re.search(r'(?<=-)[^-].*[^-](?=-*$)', s)
100000 loops, best of 3: 1.96 us per loop

In [51]: %timeit get_first_and_last(s, '-')
1000000 loops, best of 3: 1.34 us per loop

In [52]: %timeit get_indices('---abc--d--', '-')
100000 loops, best of 3: 2.53 us per loop

In [53]: %timeit get_indices(s, '-')
100000 loops, best of 3: 2.09 us per loop

In [54]: 

和最后一个运行时间最好的:

In [77]: %timeit my_get_first_and_last(s,'-')
1000000 loops, best of 3: 739 ns per loop

6 个答案:

答案 0 :(得分:2)

一个选项:

def get_indices(s, filler):
    parts = filter(None, s.split(filler))
    return s.index(parts[0]), s.index(parts[-1])+len(parts[-1])-1

>>> get_indices('---abc--d--', '-')
(3, 8)
>>> get_indices('---abc----', '-')
(3, 5)

逻辑是获得所有不是填充物的部分。然后取第一部分并找到它的索引 - 这是第一个非填充项目。然后你拿最后一部分找到它的索引+最后一部分的长度(减去1,因为索引已经占据了一个点) - 现在你有了最后一个非填充项。


另一种选择:

def get_first_and_last(s, filler):
    trimmed = s.strip(filler)
    return s.index(trimmed[0]), len(s) - 1 - s[::-1].index(trimmed[-1])

>>> get_first_and_last('---abc--d--', '-')
(3, 8)
>>> get_first_and_last('---abc----', '-')
(3, 5)

逻辑是从两侧清洁填料。然后在第一个索引的原始字符串中找到修剪字符串的开始索引。然后找到修剪后的字符串中最后一项的索引,为了确保它正常工作,我们将原始字符串反转,然后从原始字符串的长度 - 1中减去它。


选择更好的选择:

>>> import timeit
>>> timeit.Timer("get_first_and_last('---abc--d--', '-')", "from __main__ import get_first_and_last").repeat()
[1.260409049800318, 1.2315312125653757, 1.2293705754911328]
>>> timeit.Timer("get_indices('---abc--d--', '-')", "from __main__ import get_indices").repeat()
[1.7771399534411891, 1.7077849342434739, 1.698285322233577]

在这里看起来get_first_and_last是更好的选择。

答案 1 :(得分:2)

>>> s = '---abd-d--'
>>> mid = s.strip('-')
>>> si = s.index(mid[0])
>>> si, si + len(mid) - 1
(3, 7)

与Inbar Rose的最佳解决方案进行比较:

def my_get_first_and_last(s, filler):
    mid = s.strip('-')
    si = s.index(mid[0])
    return si, si + len(mid) - 1

def get_first_and_last(s, filler):
    trimmed = s.strip(filler)
    return s.index(trimmed[0]), len(s) - 1 - s[::-1].index(trimmed[-1])

>>> timeit.Timer("my_get_first_and_last('---abc--d--', '-')", "from __main__ import get_first_and_last").repeat()
[1.5137124020474033, 1.5367783393705707, 1.5110408799341997]
>>> timeit.Timer("get_first_and_last('---abc--d--', '-')", "from __main__ import get_first_and_last").repeat()
[2.3410303195946653, 2.317741755428642, 2.304720330642567]
>>> timeit.Timer("get_indices('---abc--d--', '-')", "from __main__ import get_indices").repeat()
[3.2813887808902678, 3.201712109488767, 3.150435437574032]

答案 2 :(得分:1)

获得指数的一些低效方法:

ss = ["---abc--d--", "---abc----" ]
for s in ss: 
    stripped = s.strip("-")
    idx = s.index(stripped) 
    print idx, idx + len(stripped) - 1 

给出:

3 8
3 5

但正则表达式应该更快:

import re
ss = ["---abc--d--", "---abc----" ]
re_m = re.compile(r'(?<=-)[^-].*[^-](?=-*$)')
for s in ss: 
    m = re.search(re_m, s)
    print m.start(), m.end() - 1 

答案 3 :(得分:1)

可能是re.finditer,从第一个到最后一个找到d:

import re
print next(re.finditer('a.*d', s)).span()
# (3, 9)

或更一般......(首先不是连字符,直到最后一次没有宣传):

start, end = next(re.finditer('[^-].*[^-]', s)).span()

请注意,跨度将是适合使用的半开范围,例如从原始字符串(例如s[start:end])切片子字符串。如果你确实想要最后一个字符的索引,那么你需要从end中取1。

答案 4 :(得分:0)

不是一个班轮,但不要求你在内存中创建一个新的字符串或列表(如果你执行这十亿次,这可能是有价值的。)

def find_indices(data, ignore="-"):
    start = 0
    end = len(data)-1
    for i in range(len(data)):
        if data[i] != ignore:
            start = i
            break
    for i in range(len(data)-1, 0, -1):
        if data[i] != ignore:
            end = i
            break
    return (start, end)

答案 5 :(得分:0)

oneliner解决方案:

import re
s = '---abc--d--'
[s.index(re.findall("[^-]",s)[i]) for i in [0,-1]]