在 Python 下,当您想要获取列表中第一次出现的子字符串或字符的索引时,您可以使用以下内容:
s.find("f")
但是,我想在不匹配的字符串中找到第一个字符的索引。目前,我正在使用以下内容:
iNum = 0
for i, c in enumerate(line):
if(c != mark):
iNum = i
break
有没有更有效的方法来做到这一点,比如我不知道的内置函数?
您可以使用正则表达式,例如:
>>> import re
>>> re.search(r'[^f]', 'ffffooooooooo').start()
4
[^f]
将匹配除 之外的任何字符f
,并且start()
Match 对象(由 返回re.search()
)的方法将给出匹配发生的索引。
为了确保您还可以处理空字符串或仅包含f
您想要检查的字符串,以确保re.search()
is not的结果,None
如果无法匹配正则表达式,则会发生这种情况。例如:
first_index = -1
match = re.search(r'[^f]', line)
if match:
first_index = match.start()
如果您不想使用正则表达式,那么您不会比您当前的方法做得更好。你可以使用类似的东西next(i for i, c in enumerate(line) if c != mark)
,但你需要用一个try
和except StopIteration
块来包装它来处理空行或只包含mark
字符的行。
像python一样,尽可能简单。用 python 2.x 的打印计数器替换 print(counter)
s = "ffffff5tgbh44frff"
counter = 0
for c in s:
counter = counter + 1
if c != "f":
break
print (counter)
我遇到了同样的问题,并在此处研究了解决方案的时间安排(@wwii 中的 map/list-comp 除外,它们比任何其他选项都慢得多)。我还添加了原始版本的 Cython 版本。
我在 Python v2.7 中制作并测试了所有这些。我使用的是字节字符串(而不是 Unicode 字符串)。我不确定正则表达式方法是否需要不同的东西来处理 Python v3 中的字节字符串。“标记”被硬编码为空字节。这很容易改变。
如果整个字节串是空字节,所有方法都返回 -1。所有这些都在 IPython 中进行了测试(以 % 开头的行是特殊的)。
import re
def f1(s): # original version
for i, c in enumerate(s):
if c != b'\0': return i
return -1
def f2(s): # @ChristopherMahan's version
i = 0
for c in s:
if c != b'\0': return i
i += 1
return -1
def f3(s): # @AndrewClark's alternate version
# modified to use optional default argument instead of catching StopIteration
return next((i for i, c in enumerate(s) if c != b'\0'), -1)
def f4(s): # @AndrewClark's version
match = re.search(br'[^\0]', s)
return match.start() if match else -1
_re = re.compile(br'[^\0]')
def f5(s): # @AndrewClark's version w/ precompiled regular expression
match = _re.search(s)
return match.start() if match else -1
%load_ext cythonmagic
%%cython
# original version optimized in Cython
import cython
@cython.boundscheck(False)
@cython.wraparound(False)
def f6(bytes s):
cdef Py_ssize_t i
for i in xrange(len(s)):
if s[i] != b'\0': return i
return -1
计时结果:
s = (b'\x00' * 32) + (b'\x01' * 32) # test string
In [11]: %timeit f1(s) # original version
100000 loops, best of 3: 2.48 µs per loop
In [12]: %timeit f2(s) # @ChristopherMahan's version
100000 loops, best of 3: 2.35 µs per loop
In [13]: %timeit f3(s) # @AndrewClark's alternate version
100000 loops, best of 3: 3.07 µs per loop
In [14]: %timeit f4(s) # @AndrewClark's version
1000000 loops, best of 3: 1.91 µs per loop
In [15]: %timeit f5(s) # @AndrewClark's version w/ precompiled regular expression
1000000 loops, best of 3: 845 ns per loop
In [16]: %timeit f6(s) # original version optimized in Cython
1000000 loops, best of 3: 305 ns per loop
总体而言,@ChristopherMahan 的版本比原始版本稍快(显然enumerate
比使用您自己的计数器慢)。使用next
(@AndrewClark 的替代版本)方法比原来的方法慢,即使它在单行形式中本质上是相同的。
使用正则表达式(@AndrewClark 的版本)比循环快得多,特别是如果您预编译正则表达式!
然后,如果你可以使用 Cython,它是迄今为止最快的。OP 对使用正则表达式很慢的担忧得到了验证,但 Python 中的循环甚至更慢。Cython 中的循环非常快。
现在我很好奇这两个票价如何。
>>> # map with a partial function
>>> import functools
>>> import operator
>>> f = functools.partial(operator.eq, 'f')
>>> map(f, 'fffffooooo').index(False)
5
>>> # list comprehension
>>> [c == 'f' for c in 'ffffoooo'].index(False)
4
>>>
这是一个单行:
> print([a == b for (a_i, a) in enumerate("compare_me") for
(b_i, b) in enumerate("compar me") if a_i == b_i].index(False))
> 6
> "compare_me"[6]
> 'e'