Python:确定一组(类似)string的前缀
我有一组string,例如
my_prefix_what_ever my_prefix_what_so_ever my_prefix_doesnt_matter
我只想find这些string中最长的公共部分,这里是前缀。 在上面的结果应该是
my_prefix_
string
my_prefix_what_ever my_prefix_what_so_ever my_doesnt_matter
应该导致前缀
my_
在Python中有没有一种相对无痛的方式来确定前缀(而不必手动迭代每个字符)?
PS:我正在使用Python 2.6.3。
永远不要重写什么提供给你: os.path.commonprefix
正是这样做:
返回列表中所有path前缀的最长path前缀(逐个字符)。 如果列表为空,则返回空string(
''
)。 请注意,这可能会返回无效path,因为它一次处理一个字符。
与其他答案进行比较,代码如下:
# Return the longest prefix of all list elements. def commonprefix(m): "Given a list of pathnames, returns the longest common leading component" if not m: return '' s1 = min(m) s2 = max(m) for i, c in enumerate(s1): if c != s2[i]: return s1[:i] return s1
Ned Batchelder可能是对的。 但为了它的乐趣,这里是使用itertools
更有效的phimuemue的答案版本。
import itertools strings = ['my_prefix_what_ever', 'my_prefix_what_so_ever', 'my_prefix_doesnt_matter'] def all_same(x): return all(x[0] == y for y in x) char_tuples = itertools.izip(*strings) prefix_tuples = itertools.takewhile(all_same, char_tuples) ''.join(x[0] for x in prefix_tuples)
作为可读性的冒犯,这是一个单行版本:)
>>> from itertools import takewhile, izip >>> ''.join(c[0] for c in takewhile(lambda x: all(x[0] == y for y in x), izip(*strings))) 'my_prefix_'
这是我的解决scheme:
a = ["my_prefix_what_ever", "my_prefix_what_so_ever", "my_prefix_doesnt_matter"] prefix_len = len(a[0]) for x in a[1 : ]: prefix_len = min(prefix_len, len(x)) while not x.startswith(a[0][ : prefix_len]): prefix_len -= 1 prefix = a[0][ : prefix_len]
以下是一个工作,但可能相当低效的解决scheme。
a = ["my_prefix_what_ever", "my_prefix_what_so_ever", "my_prefix_doesnt_matter"] b = zip(*a) c = [x[0] for x in b if x==(x[0],)*len(x)] result = "".join(c)
对于一小串琴弦来说,上述没有任何问题。 但是对于更大的集合,我个人会编写另一个手动解决scheme,逐个检查每个字符,并在有差异时停止。
在algorithm上,这产生相同的过程,但是,可以避免构build列表c
。
出于好奇,我想出了另一种方法来做到这一点:
def common_prefix(strings): if len(strings) == 1:#rule out trivial case return strings[0] prefix = strings[0] for string in strings[1:]: while string[:len(prefix)] != prefix and prefix: prefix = prefix[:len(prefix)-1] if not prefix: break return prefix strings = ["my_prefix_what_ever","my_prefix_what_so_ever","my_prefix_doesnt_matter"] print common_prefix(strings) #Prints "my_prefix_"
正如Ned指出,使用os.path.commonprefix
可能更好,这是一个相当优雅的function。
第二行对inputstring中的每个字符都使用了reduce函数。 它返回N + 1个元素的列表,其中N是最短inputstring的长度。
批次中的每个元素都是(a)input字符,如果所有inputstring在该位置匹配,或者(b)无。 lot.index(None)是批中第一个None的位置:通用前缀的长度。 那是常用的前缀。
val = ["axc", "abc", "abc"] lot = [reduce(lambda a, b: a if a == b else None, x) for x in zip(*val)] + [None] out = val[0][:lot.index(None)]
这是使用OrderedDict最小代码的另一种方法。
import collections import itertools def commonprefix(instrings): """ Common prefix of a list of input strings using OrderedDict """ d = collections.OrderedDict() for instring in instrings: for idx,char in enumerate(instring): # Make sure index is added into key d[(char, idx)] = d.get((char,idx), 0) + 1 # Return prefix of keys while value == length(instrings) return ''.join([k[0] for k in itertools.takewhile(lambda x: d[x] == len(instrings), d)])
这是一个简单的清洁解决scheme。 这个想法是使用zip()函数将所有字符排列在第一个字符的列表中,第二个字符的列表中,…第n个字符的列表中。 然后迭代每个列表来检查它们是否只包含1个值。
a = ["my_prefix_what_ever", "my_prefix_what_so_ever", "my_prefix_doesnt_matter"] list = [all(x[i] == x[i+1] for i in range(len(x)-1)) for x in zip(*a)] print a[0][:list.index(0) if list.count(0) > 0 else len(list)]
输出:my_prefix_