检查Python中的字符串s
只包含一个字符(比如'A'
)的有效方法是什么?像all_equal(s, 'A')
这样的行为就像这样:
all_equal("AAAAA", "A") = True
all_equal("AAAAAAAAAAA", "A") = True
all_equal("AAAAAfAAAAA", "A") = False
两种看似效率低下的方法是:首先将字符串转换为列表并检查每个元素,然后再使用正则表达式。是否有更有效的方法,或者这些是Python中最好的方法?感谢。
答案 0 :(得分:106)
这是迄今为止最快的,比偶数count()
快几倍,只需用优秀的mgilson's timing suite计算时间:
s == len(s) * s[0]
这里所有的检查都在Python C代码中完成,只需:
字符串越长,时间奖励越大。但是,正如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
抽烟set
和all
,并打败{{ 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
解决方案的情况。
如果内存可能是一个问题,那么您需要避免count
,str.translate
和str.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', ''))