2009-12-09 6 views
0

Je suis en train de lire une liste de chaînes, chacune se rapportant à un nom de fichier. Cependant, chaque chaîne est moins l'extension. Je suis venu avec le code suivant:Comment utiliser re pour rechercher des éléments dans une liste dans une autre liste en Python

import re 
item_list = ['item1', 'item2'] 
search_list = ['item1.exe', 'item2.pdf'] 
matches = [] 
for item in item_list: 
    # Match item in search_list using re - I assume this is the best way to do this 
    regex = re.compile("^"+item+"\.") 
    for file in search_list: 
     if regex.match(file): 
      matches.append((item, file)) 

En ce qui concerne les matchs en double, je ne suis pas extrêmement inquiet au sujet de deux fichiers être nommé « foo.bar » et « foo.foo.bar ». Cela dit, y a-t-il une meilleure façon de faire cela?

Merci.

+0

Utile pour savoir combien d'éléments dans chacune des deux listes, et à quelle fréquence le code sera appelé. – Will

+0

@Will, je dirais qu'il y aurait des milliers de fichiers dans la liste. La liste devrait être légèrement (5% environ) plus grande que le nombre réel de fichiers dans le répertoire. – Alex

Répondre

2

Vous pouvez combiner tous les éléments dans un regexp comme celui-ci qui sera plus efficace

import re 
item_list = ['item1', 'item2'] 
regex = re.compile("^("+"|".join(item_list)+")\.") 
search_list = ['item1.exe', 'item2.pdf'] 
matches = [] 
for file in search_list: 
    match = regex.match(file) 
    if match: 
     matches.append((match.group(1), file)) 

A Une meilleure solution pourrait être d'analyser les noms de fichiers en utilisant les fonctions os.path pour analyser les noms de base et les chercher dans un ensemble.

+0

Si les éléments peuvent contenir une ponctuation spéciale regex comme '.', vous devrez' re.escape' chaque élément de 'item_list' avant de rejoindre. – bobince

+0

Merci Nick, ce post mérite une centaine de votes utiles! Trouvé le module timeit et exécuté des tests basés sur mon algorithme original, l'algorithme de Dave Kirby, et le vôtre. Les résultats sont les suivants: alex_k: 15,93 dave_kirby: 6,98 nick_craig_wood: 0,24 – Alex

0

Voici une autre façon de le faire qui est probablement plus rapide que le code original de Alex:

item_list = ['item1', 'item2'] 
search_list = ['item1.exe', 'item2.pdf'] 
matches = [] 
for item in item_list: 
    for filename in search_list: 
     if filename.partition(".")[0] == item: 
      matches.append((item,filename)) 
2

Utilisez splitext pour obtenir le nom de fichier sans l'extension:

import os.path 

for item in item_list: 
    for filename in search_list: 
     if item == os.path.splitext(filename)[0]: 
      matches.append((item, file)) 

Il est plus correct, mais il est aussi plus facile de comprendre ce que votre intention est de lire le code. Sinon, si vous voulez que foo corresponde à foo.bar.txt, utilisez plutôt filename.startswith (item + '.').

+0

+1 pour splitext. Fait exactement ce qu'il dit; plus lisible que regex. – bobince

0

Je pense que vous devriez utiliser .rsplit(".",1) à cette fin, regex ne sont pas exagérées?

>>> item_list = ['item1', 'item2','item3'] 
>>> search_list = ['item1.exe', 'item2.pdf','item9999.txt'] 
>>> 
>>> [(x.rsplit(".",1)[0],x) for x in search_list if x.rsplit(".",1)[0] in item_list] 
[('item1', 'item1.exe'), ('item2', 'item2.pdf')] 

ou avec boucle

matches=[] 
for x in search_list: 
    y=x.rsplit(".",1)[0] 
    if y in item_list: 
     matches.append((y,x)) 
1

Vous n'avez pas besoin d'utiliser un regex pour cela car vous faites des matchs de chaîne exacte (les caractères génériques, groupes, etc) - vous pouvez utiliser str.startsWith (..) à la place. Cela équivaut à votre code:

for item in item_list: 
    match = item + "." 
    for file in search_list: 
     if file.startswith(match) 
      matches.append((item, file)) 

Cependant la suggestion de Nick Craig-Wood de compiler tous les matchs en une seule expression régulière peut être plus efficace - je vous suggère de référence à la fois si la vitesse est un problème.

+0

Tous les outils/commandes pour aider benchmark serait un +1! – Alex

0
>>> for file in search_list: 
... tomatch=file.split(".")[0] 
... if tomatch in item_list: 
...  found=item_list.index(tomatch) 
...  matches.append((file, item_list[found])) 
... 
>>> print matches 
[('item1.exe', 'item1'), ('item2.pdf', 'item2')] 
>>> 

Pas besoin de regex.

1

Évitez re sauf si vous en avez vraiment besoin. Pour la correspondance de chaîne simple, vous n'en avez pas vraiment besoin.

La réponse de Mark Byers duplique le comportement d'origine de la conservation matches dans item_list. Si vous n'avez pas besoin, vous pouvez le faire encore plus simplement/rapidement:

for file in search_list: 
    item= os.path.splitext(file)[0] 
    if item in item_list: 
     matches.append((item, file)) 

Si vous n'avez pas besoin de garder la (item) adaptée soit (car il est redondant du nom de fichier de toute façon), vous avez a obtenu un one-liner:

matches= [file for file in search_list if os.path.splitext(file)[0] in item_list] 
+0

Ils doivent être jumelés, mais merci de donner un bon exemple de doublure! – Alex

Questions connexes