在一个字符串中查找一个字符的所有位置

6

我尝试使用基本技能集在Python字符串中找到一个字符的所有索引号。例如,如果我有字符串“Apples are totally awesome”,我想找到字符串中'a'的位置。 我的理想输出结果如下:

0
7
14
19

这些是字符串中出现'a'的所有位置(我认为)

这是我目前的代码:

sentence = input("Input a string: ")
for ch in sentence:
    x = sentence.find('o')


print(x)

我在寻找 'o' 而不是 'a'. 我的思路是,在字符串中,对于每个字符,find 函数将返回 'o' 的位置。因为我不知道输入字符串会有多长,所以我使用了一个 for 循环。我能够找到并打印出第一个 'o' 的实例,但不能找到所有的 'o'. 我该怎么办?提前致谢!


你应该使用 for index, ch in enumerate(sentence) 迭代字符;然后进行简单的相等性检查即可确定是否打印 index - Amadan
https://docs.python.org/3/library/stdtypes.html#str.find - Stephen Rauch
4个回答

8

使用enumerate是标准的方法。但是,在时间紧迫的操作中,您可以利用str.find的速度优势。

代码

def find_all(s, c):
    idx = s.find(c)
    while idx != -1:
        yield idx
        idx = s.find(c, idx + 1)

print(*find_all('Apples are totally awesome', 'o')) # 12 23

为了优雅起见并考虑到非常大的字符串,我将上面的内容转换为生成器。但如果需要,它当然可以转换为list

基准测试

这是使用enumerate和列表推导式的解决方案进行基准测试的结果。两种解决方案都具有线性时间复杂度,但str.find显著更快。

import timeit

def find_all_enumerate(s, c):
    return [i for i, x in enumerate(s) if c == x]

print(
    'find_all:',
    timeit.timeit("list(find_all('Apples are totally awesome', 'o'))",
                  setup="from __main__ import find_all")
)

print(
    'find_all_enumerate:',
    timeit.timeit("find_all_enumerate('Apples are totally awesome', 'o')",
                  setup="from __main__ import find_all_enumerate")
)

输出

find_all: 1.1554179692960915
find_all_enumerate: 1.9171753468076869

那是一个很棒的解决方案。 - Stephen Rauch
@StephenRauch 嗯,你在另一条评论中指出了那个方向,所以你应该得到一些功劳。 - Olivier Melançon
你能提供一个真实的算法分析,证明这种方法始终更高效吗?你知道这些函数是如何实现的吗?单个timeit测试对我来说并不具有令人信服的证据。 - ubadub
1
@udadub 两种算法都对单个字符进行单次遍历。只是发现find是在C级别完成的。这消除了索引和项分配的开销,使其以恒定速度更快。时间复杂度相同。 - Olivier Melançon
1
@ubadub 此外,str.find使用Boyer-Moore,其时间复杂度为O(nm)。 因此,在此问题中,当m = 1时,它是线性的。 - Olivier Melançon
1
如果 c == x,而不是 if c == 'a'。 - Anass

2
使用列表推导式,为了更好地实现编程:
[ind for ind, ch in enumerate(sentence) if ch.lower() == 'a']

将返回您想要的所有数字的列表。按需要打印。

根据您的示例,我假设您不关心大小写,因此使用lower()函数调用。使用Python 3的星号展开运算符(*),您可以将所有内容都放在一行中;但是,这留给读者作为练习。


1
这是一个很好的位置用于枚举,它允许我们在循环时获取索引和项目,因此如果我们匹配项目,我们可以得到相应的索引,同时使用.lower()有助于避免匹配大小写问题。保留html标签。
s = 'Apples are totally awesome'

l = [idx for idx, item in enumerate(s.lower()) if 'o' in item]

扩展循环:

l = []
for idx, item in enumerate(s.lower()):
    if 'o' in item:
        l.append(idx)
/python/stack$ python3.7 sum.py 
[12, 23]

你真的应该考虑使用 find - Stephen Rauch
1
@StephenRauch find 只会找到第一个出现的实例。 - Olivier Melançon
@OlivierMelançon,find命令有一个起点,可以让你找到下一个。 - Stephen Rauch
Stephen,你能解释一下为什么建议使用find而不是enumerate吗? - ubadub
天啊!非常抱歉,这是我有很长一段时间以来最开心的一天,我必须要发泄一下,我刚刚达到了1k声望,我知道对于一些人来说可能毫无意义,但是我的生活在过去的几个月里确实陷入了低谷,也许是我29年来遇到的最糟糕的时期,33天前,我决定学习编程,而我深深地爱上了 Stack 和学习的过程。这对我来说是一个很大的目标,你们可能觉得并不重要,但对我来说它是一束光明,感谢你们所有人的帮助,从你们身上学到东西真的是一种祝福<3 - vash_the_stampede

0
一个解决方案是以以下方式使用find方法。
y="Apples are totally awesome"
    b=0
    for i in range (y.count(o)):
        a=y.find('o' , b)
        print(a)
        b=a+1
    #Returns all the positions of o

1
请添加一些清晰的解释,说明您试图实现什么。可能包括一个输入和一个输出示例。 - Selnay

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接