有效地检查该字符串是否包含Python中的一个字符

时间:2013-01-14 15:02:08

标签: python string

检查Python中的字符串s只包含一个字符(比如'A')的有效方法是什么?像all_equal(s, 'A')这样的行为就像这样:

all_equal("AAAAA", "A") = True

all_equal("AAAAAAAAAAA", "A") = True

all_equal("AAAAAfAAAAA", "A") = False

两种看似效率低下的方法是:首先将字符串转换为列表并检查每个元素,然后再使用正则表达式。是否有更有效的方法,或者这些是Python中最好的方法?感谢。

8 个答案:

答案 0 :(得分:106)

这是迄今为止最快的,比偶数count()快几倍,只需用优秀的mgilson's timing suite计算时间:

s == len(s) * s[0]

这里所有的检查都在Python C代码中完成,只需:

  • 分配len(s)字符;
  • 用第一个字符填充空格;
  • 比较两个字符串。

字符串越长,时间奖励越大。但是,正如mgilson所写,它会创建一个字符串的副本,因此如果您的字符串长度是数百万个符号,则可能会出现问题。

正如我们从时序结果中看到的,通常解决任务的最快方法不会为每个符号执行任何Python代码。但是,set()解决方案也完成了Python库C代码中的所有工作,但它仍然很慢,可能是因为通过Python对象接口操作字符串。

UPD:关于空字符串案例。如何处理它在很大程度上取决于任务。如果任务是"检查字符串中的所有符号是否相同",s == len(s) * s[0]是有效答案(没有符号表示错误,异常就可以)。如果任务是"检查是否只有一个唯一符号",如果您更喜欢接收布尔值,则空字符串应该为False,答案为s and s == len(s) * s[0]bool(s) and s == len(s) * s[0]。最后,如果我们将任务理解为"检查是否没有不同的符号",则空字符串的结果为True,答案为not s or s == len(s) * s[0]

答案 1 :(得分:40)

>>> s = 'AAAAAAAAAAAAAAAAAAA'
>>> s.count(s[0]) == len(s)
True

这不会短路。一个做短路的版本是:

>>> all(x == s[0] for x in s)
True

但是,我觉得由于优化的C实现,非短路版本可能在某些字符串上表现更好(取决于大小等)


这是一个简单的timeit脚本,用于测试发布的其他一些选项:

import timeit
import re

def test_regex(s,regex=re.compile(r'^(.)\1*$')):
    return bool(regex.match(s))

def test_all(s):
    return all(x == s[0] for x in s)

def test_count(s):
    return s.count(s[0]) == len(s)

def test_set(s):
    return len(set(s)) == 1

def test_replace(s):
    return not s.replace(s[0],'')

def test_translate(s):
    return not s.translate(None,s[0])

def test_strmul(s):
    return s == s[0]*len(s)

tests = ('test_all','test_count','test_set','test_replace','test_translate','test_strmul','test_regex')

print "WITH ALL EQUAL"
for test in tests:
    print test, timeit.timeit('%s(s)'%test,'from __main__ import %s; s="AAAAAAAAAAAAAAAAA"'%test)
    if globals()[test]("AAAAAAAAAAAAAAAAA") != True:
        print globals()[test]("AAAAAAAAAAAAAAAAA")
        raise AssertionError

print
print "WITH FIRST NON-EQUAL"
for test in tests:
    print test, timeit.timeit('%s(s)'%test,'from __main__ import %s; s="FAAAAAAAAAAAAAAAA"'%test)
    if globals()[test]("FAAAAAAAAAAAAAAAA") != False:
        print globals()[test]("FAAAAAAAAAAAAAAAA")
        raise AssertionError

在我的机器上(OS-X 10.5.8,core2duo,python2.7.3),这些设计的(短)字符串,str.count抽烟setall,并打败{{ 1}}一点点,但被str.replace淘汰,而str.translate目前处于领先优势:

strmul

不同系统和不同字符串之间的时间可能略有差异(甚至显着不同),因此值得研究一下您计划传递的实际字符串。

最终,如果你的WITH ALL EQUAL test_all 5.83863711357 test_count 0.947771072388 test_set 2.01028490067 test_replace 1.24682998657 test_translate 0.941282987595 test_strmul 0.629556179047 test_regex 2.52913498878 WITH FIRST NON-EQUAL test_all 2.41147494316 test_count 0.942595005035 test_set 2.00480484962 test_replace 0.960338115692 test_translate 0.924381017685 test_strmul 0.622269153595 test_regex 1.36632800102 达到了最好的情况,并且你的字符串足够长,你可能想要考虑那个。这是一个更好的算法...我会避免使用all解决方案,因为我没有看到任何可能超出set解决方案的情况。

如果内存可能是一个问题,那么您需要避免countstr.translatestr.replace,因为这些会创建第二个字符串,但这些日子通常不会引起关注

答案 2 :(得分:15)

您可以转换为一个集合并检查只有一个成员:

len(set("AAAAAAAA"))

答案 3 :(得分:12)

尝试使用内置函数all

all(c == 'A' for c in s)

答案 4 :(得分:6)

为此问题添加其他解决方案

>>> not "AAAAAA".translate(None,"A")
True

答案 5 :(得分:5)

如果需要检查字符串中的所有字符是否相同且是否等于给定字符,则需要删除所有重复项并检查最终结果是否等于单个字符。

>>> set("AAAAA") == set("A")
True

如果您想查找是否有重复,只需检查长度

>>> len(set("AAAAA")) == 1
True

答案 6 :(得分:3)

到目前为止有趣的答案。这是另一个:

flag = True
for c in 'AAAAAAAfAAAA':
    if not c == 'A': 
        flag = False
        break

我能想到的唯一优势是,如果找到不一致的字符,则不需要遍历整个字符串。

答案 7 :(得分:2)

not len("AAAAAAAAA".replace('A', ''))