J'ai un ensemble de chaînes, par exemple.
my_prefix_what_ever
my_prefix_what_so_ever
my_prefix_doesnt_matter
Je veux simplement trouver la plus longue portion commune de ces chaînes, ici le préfixe. Dans ce qui précède, le résultat devrait être
my_prefix_
Les cordes
my_prefix_what_ever
my_prefix_what_so_ever
my_doesnt_matter
devrait aboutir au préfixe
my_
Existe-t-il un moyen relativement simple de déterminer le préfixe en Python (sans avoir à itérer manuellement chaque caractère)?
PS: J'utilise Python 2.6.3.
Ne réécrivez jamais ce qui vous est fourni: os.path.commonprefix
fait exactement ceci:
Renvoie le préfixe de chemin le plus long (pris Caractère par caractère) qui est un préfixe de tous les chemins de la liste. Si liste est vide, retourne la chaîne vide (
''
). Notez que cela peut retourner chemins non valides, car cela fonctionne caractère par caractère.
Pour la comparaison avec les autres réponses, voici le code:
# 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 a probablement raison. Mais pour le plaisir, voici une version plus efficace de la réponse de phimuemue en utilisant itertools
.
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)
Comme un affront à la lisibilité, voici une version d'une ligne :)
>>> 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_'
Voici ma solution:
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]
Ce qui suit est une solution de travail, mais probablement assez inefficace.
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)
Pour les petits ensembles de chaînes, ce qui précède ne pose aucun problème. Mais pour les plus grands ensembles, je codifierais personnellement une autre solution manuelle qui vérifie chaque caractère les uns après les autres et s’arrête en cas de différences.
Algorithmiquement, cela produit la même procédure, cependant, on pourrait peut-être éviter de construire la liste c
.
Juste par curiosité, j'ai trouvé un autre moyen de le faire:
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_"
Comme Ned l'a souligné, il est probablement préférable d'utiliser os.path.commonprefix
, qui est une fonction assez élégante.
Voici une autre façon de faire cela en utilisant OrderedDict avec un code minimal.
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)])
Voici une solution simple et propre. L'idée est d'utiliser la fonction Zip () pour aligner tous les caractères en les plaçant dans une liste de 1ers caractères, liste de 2èmes caractères, ... liste de nième caractères. Ensuite, parcourez chaque liste pour vérifier si elles ne contiennent qu'une seule valeur.
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)]
sortie: mon_prefix_