文本处理 - 检查字符串中是否包含某字符集中的字符

1. 字符串是否包含某字符集中的字符

最简单,兼具清晰,快速,通用(不限于字符串,只要是序列都可以)的版本如下:

1 def containsAny(seq, aset):
2     for c in seq:
3         if c in aset: return True
4     return False

也可以使用高级点的itertools模块方法来提高一点点性能,其本质和上面的方法是一样的:

1 import itertools
2 def containsAny(seq, aset):
3     for c in itertools.ifilter(aset.__contains__, seq):
4         return True
5     return False

只所以说这两种方法本质上是一样的是因第一种方法在执行if c in aset,内部其实执行的是if itertools.ifilter(aset.__contains__, c)。

考虑到这个问题和集合有关系,理所当然考虑使用内建set类型来解决该问题:

1 def containsAny(seq, aset):
2     return bool(set(aset).intersection(seq))

分析上述方法,假设seq = 'uzero', aset = 'uov',第一种方法在遍历到seq的第一个字符'u',就会发现'u'在字符集'uov中',函数返回True,后面的字符不再判断。第二种方法具有同样的性质。这种性质就像or的短路特性一样只要前面的一个断言是真后面的不再计算。第三个方法中,intersection方法会返回一个新的集合,该集合是seq中所有出现在aset中的字符集,intersection为了获取所有字符集,不可避免的会遍历整个seq。假设seq非常长且出现在aset中的字符在seq中非常靠前的话,一,二两种方法的短路特性就会发挥很好的作用,不必遍历整个seq,遇到aset中的任意字符马上返回。第三种方法不得不慢悠悠的便利整个seq。当然如果seq不包含aset字符集中的字符,或者包含的字符在seq中非常的靠后,那么三者的性能基本没啥差别。

2. 字符串是否包含某个字符集中所有的字符

显然seq序列必须全部被遍历到,所以想利用短路提高性能是不大可能。考虑使用循环进行判断的话,那么需要两层循环,外层用来遍历aset,内层遍历seq,代码的复杂度就会增加。这时正是使用内建类型set的好时机:

1 def containsAll(seq, aset):
2     return not set(aset).difference(seq)

difference方法返回aset中不属于seq的元素集合。当difference方法返回空集合时,那么说明seq包含了所有aset集合中的元素,这时containsAll返回真,否则说明aset集合中的某个字符不再seq中,containsAll返回False。

举例说明下difference方法:

1 >>> L1 = '1233'
2 >>> L2 = '1234'
3 >>> set(L1).difference(L2)
4 set([])
5 >>> set(L2).difference(L1)
6 set(['4'])

L2包含了L1,所以第一个difference函数返回空集合,L1不包含L2,第二个difference函数返回L2中不属于L1的元素集合['4']。

也可以使用上面实现的containsAll函数说明difference的原理:

1 >>> containsAll(L1, L2)
2 False
3 >>> containsAll(L2, L1)
4 True

上面这些方法举的例子都是字符串,但是实现的函数都是通用的,对于任何python序列(列表,tuple等)都有效。如果仅仅操作字符串,也许不需要这么通用的函数,那么使用基于字符串的translate函数和string.maketrans函数可以提高性能,且灵活易用:

1 import string
2 def containsAny(seq, aset):
3     return len(aset) != len(aset.translate(None, seq))
4 
5 def containsAll(seq, aset):
6     return not aset.translate(None, seq)

aset.translate(notrans, seq)方法返回的字符串是aset的子串且是由所有不属于seq的字符组成,逻辑和set.difference是一样的。当aset.translate(None, seq)方法返回的字符串的长度和aset的长度相等时,说明aset.translate(None, seq)没有从aset中删除字符,也就是说seq不包含任何aset中的字符,containsAny返回False,否则两者长度不相等,说明seq包含aset中的字符,containsAny返回True;当aset.translate(None, seq)返回的字符串是空时,说明aset中的字符全部被删除返回了一个空的字符串,也就是说aset字符全部被包含在seq中,containsAll函数返回True。

 

 

 

posted @ 2013-01-24 17:44  uzero  阅读(952)  评论(0编辑  收藏  举报