在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
以外的任何字符,而由re.search()
返回的Match对象的start()
方法将给出发生匹配的索引。
为了确保您还可以处理空字符串或仅包含f
字符的字符串,您需要检查re.search()
的结果不是None
,如果无法匹配正则表达式,则该方法将返回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
字符组成的行。
我曾经遇到过同样的问题,并研究了这里的解决方案的时间(除了@wwii的地图/列表补充选项,它们比任何其他选项都要慢得多)。我还添加了原始版本的Cython版本。
我在Python v2.7中进行了所有制作和测试。我正在使用字节字符串(而不是Unicode字符串)。我不确定正则表达式方法是否需要与Python v3中的字节字符串一起使用。'mark'硬编码为null字节。这可以很容易地更改。
如果整个字节字符串是null-byte,则所有方法都返回-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中的循环非常快。
f1
、f2
和f3
的时间大致相同,约为200微秒(其中f3
最快,f2
最慢)。f4
和f5
大约为26微秒,预编译只稍微快一点。f6
版本仍然是最快的,但与正则表达式版本相比,其差距不再那么大。 - coderforlife尽可能像Python一样简单。 将print(counter)替换为Python 2.x的print counter。
s = "ffffff5tgbh44frff"
counter = 0
for c in s:
counter = counter + 1
if c != "f":
break
print (counter)
这里是一行代码:
> 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'
现在我很好奇这两个怎么样。
>>> # 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
>>>