我正在寻找一个oneliner来获取字符串中第一个和最后一个非连字符的索引。在以下示例中,索引为a
和d
。我会删除任何前导或尾随连字符-
。
我必须做几十亿次这个操作。
---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
答案 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]]