2013-09-22 5 views
2

Je suis un débutant, été d'apprendre python pendant quelques mois comme mon premier langage de programmation. Je cherche un motif à partir d'un fichier texte. Ma première tentative a été utilisant l'expression rationnelle, qui fonctionne, mais a une limitation:Rechercher un motif dans un fichier texte à partir de plusieurs éléments dans plusieurs listes?

import re 

noun_list = ['bacon', 'cheese', 'eggs', 'milk', 'list', 'dog'] 
CC_list = ['and', 'or'] 

noun_list_pattern1 = r'\b\w+\b,\s\b\w+\b,\sand\s\b\w+\b|\b\w+\b,\s\b\w+\b,\sor\s\b\w+\b|\b\w+\b,\s\b\w+\b\sand\s\b\w+\b|\b\w+\b,\s\b\w+\b,\saor\s\b\w+\b' 

with open('test_sentence.txt', 'r') as input_f: 
    read_input = input_f.read() 
    word = re.findall(noun_list_pattern1, read_input) 
    for w in word: 
     print w 
else: 
    pass 

Donc, à ce stade, vous demandez peut-être pourquoi les listes dans ce code, car ils ne sont pas utilisés. Eh bien, je me suis creusé la cervelle, en essayant toutes sortes de boucles et si les déclarations dans les fonctions pour essayer de trouver une raison de répliquer le modèle regex, mais en utilisant les listes. La limitation avec regex est que le code \b\w+\w\ trouvé un certain nombre de fois dans `noun_list_pattern 'trouve seulement des mots - des mots - mais pas des noms spécifiques. Cela pourrait augmenter les faux positifs. Je veux réduire les choses plus en utilisant les éléments de la liste ci-dessus au lieu de l'expression rationnelle.

Depuis que j'ai 4 regex différentes dans le modèle regex (il contient 4 |), je vais aller avec 1 d'entre eux ici. Donc, je dois trouver un motif tel que:

'noun in noun_list' + ', ' + 'noun in noun_list' + ', ' + 'C in CC_list' + ' ' + 'noun in noun_list

De toute évidence, la ligne cité code ci-dessus n'est pas vrai code python, mais est un experession de mes réflexions sur le match nécessaire. Où je dis noun in noun_list Je veux dire une itération à travers la liste de noms; C in CC_list est une itération à travers la liste CC_list; , est une correspondance de chaîne littérale pour une virgule et un espace.

Espérons que je me suis fait clair!

Voici le contenu du fichier test_sentence.txt que j'utilise:

I need to buy are bacon, cheese and eggs. 
I also need to buy milk, cheese, and bacon. 
What's your favorite: milk, cheese or eggs. 
What's my favorite: milk, bacon, or eggs. 
+0

Pourriez-vous mefficher un exemple des données que vous souhaitez suivre? – jrd1

+0

oups! oui va faire, oublié à ce sujet. –

+2

Etes-vous en train d'analyser la structure de chaque phrase et de regrouper les semblables? Si oui, vous voudrez peut-être essayer la bibliothèque nltk: http://nltk.org/ Ils ont aussi un document gratuit bien écrit (vient comme un livre) en tant que guide débutant. – Mai

Répondre

2

Cassez votre problème un peu. D'abord, vous avez besoin d'un modèle qui correspondra aux mots de votre liste, mais pas d'autre. Vous pouvez accomplir cela avec l'opérateur d'alternance | et les mots littéraux. red|green|blue, par exemple, correspond à "red", "green" ou "blue", mais pas "purple". Joignez-vous à la liste de nom avec ce caractère, et ajouter le mot métacaractères frontière avec des parenthèses pour regrouper les alternances:

noun_patt = r'\b(' + '|'.join(nouns) + r')\b' 

Faites la même chose pour votre liste de conjonctions:

conj_patt = r'\b(' + '|'.join(conjunctions) + r')\b' 

Le match global que vous voulez de faire est « un ou plusieurs noun_patt correspondance, chacun étant éventuellement suivi d'une virgule, suivie d'une correspondance pour le conj_patt puis une plus noun_patt match. » assez facile pour une expression régulière:

patt = r'({0},?)+{1} {0}'.format(noun_patt, conj_patt) 

Vous ne voulez pas vraiment utiliser re.findall(), mais re.search(), puisque vous n'attend un match par ligne:

for line in lines: 
...  print re.search(patt, line).group(0) 
... 
bacon, cheese and eggs 
milk, cheese, and bacon 
milk, cheese or eggs 
milk, bacon, or eggs 

Comme une note, vous re proche de, sinon frottant contre, les limites des expressions régulières en ce qui concerne l'analyse de l'anglais. Plus complexe que cela, et vous voudrez regarder dans l'analyse réelle, peut-être avec NLTK.

+0

Merci Josh =) C'est parfait. En tant que newb je n'étais pas au courant de la méthode '.format', très utile. J'ai aussi appris quelques trucs intéressants de la façon dont vous avez formaté l'expression rationnelle pour les listes noun_patt et conj_patt. Comme j'ai besoin de plus d'une correspondance par ligne (ou fichier entier) en fonction de la façon dont j'ai lu le fichier, alors je peux jouer un peu avec 're.findall()' et 're.search() 'pour obtenir ce que je besoin d'une situation donnée. Cette approche regex va être super utile pour d'autres choses similaires dont j'ai besoin. Merci encore =) –

+0

Josh, je me demande aussi si cela ne vous dérange pas de m'expliquer le positionnement de la parenthèse dans 'noun_patt' et' conj_patt'? J'ai essayé de regarder les documents, mais je ne pouvais pas le comprendre. Ma compréhension était que parenthèse dans regex seulement retourné cette partie de la correspondance, de même le code ne retourne pas les '\ b 'qui se trouvent en dehors de la parenthèse? Aussi, je suis curieux de savoir comment les parenthèses sont positionnées entre deux guillemets simples, tels que les segments 'r '\ b ('' et '') \ b'' de la liste' noun_patt'. C'est comme la parenthèse «entre» entre guillemets simples –

+0

1 chose de plus =) J'ai essayé ce script en ouvrant le fichier texte avec la méthode '.read()', en utilisant la fonction 're.findall()' suivie par une boucle for pour imprimer chaque 'find' si le 're.findall()'. Le résultat était un peu éteint, mais proche. Pour une raison quelconque, nous obtenons un nom en double, la première ligne imprimée: '('cheese', 'cheese', 'and', 'eggs')'. Nous obtenons 2 variantes de «fromage» - avec et sans espace arrière. Les autres lignes imprimées suivant dans la même couleur. Vous ne savez pas pourquoi l'utilisation de la méthode '.read()' modifie la correspondance de l'expression régulière? J'ai joué un peu avec la regex, sans résultat souhaité. –

2

En réalité, vous n'avez pas nécessairement besoin d'expressions régulières, car il existe plusieurs façons de le faire en utilisant uniquement vos listes d'origine.

noun_list = ['bacon', 'cheese', 'eggs', 'milk', 'list', 'dog'] 
conjunctions = ['and', 'or'] 

#This assumes that file has been read into a list of newline delimited lines called `rawlines` 
for line in rawlines: 
    matches = [noun for noun in noun_list if noun in line] + [conj for conj in conjunctions if conj in line] 
    if len(matches) == 4: 
     for match in matches: 
      print match 

La raison pour laquelle le numéro de correspondance est 4, est que 4 est le nombre correct de correspondances. (Notez que cela pourrait aussi être le cas pour les noms ou les conjonctions répétés).

EDIT:

Cette version imprime les lignes qui correspondent et les mots correspondants. Également fixé le mot multiple possible problème de correspondance:

words_matched = [] 
matching_lines = [] 

for l in lst: 
    matches = [noun for noun in noun_list if noun in l] + [conj for conj in conjunctions if conj in l] 
    invalid = True 
    valid_count = 0 
    for match in matches: 
     if matches.count(match) == 1: 
      valid_count += 1 
    if valid_count == len(matches): 
     invalid = False 

    if not invalid: 
     words_matched.append(matches) 
     matching_lines.append(l) 

for line, matches in zip(matching_lines, words_matched): 
    print line, matches 

Cependant, si cela ne vous convient pas, vous pouvez toujours construire le regex comme suit (en utilisant le module itertools):

#The number of permutations choices is 3 (as revealed from your examples) 
for nouns, conj in itertools.product(itertools.permutations(noun_list, 3), conjunctions): 
    matches = [noun for noun in nouns] 
    matches.append(conj) 
    #matches[:2] is the sublist containing the first 2 items, -1 is the last element, and matches[2:-1] is the element before the last element (if the number of nouns were more than 3, this would be the elements between the 2nd and last). 
    regex_string = '\s,\s'.join(matches[:2]) + '\s' + matches[-1] + '\s' + '\s,\s'.join(matches[2:-1]) 
    print regex_string 
    #... do regex related matching here 

La mise en garde de cette méthode est que c'est une force brute pure car elle génère toutes les combinaisons possibles (permutations de lecture) des deux listes qui peuvent ensuite être testées pour voir si chaque ligne correspond. Par conséquent, il est horriblement lent, mais dans cet exemple qui correspond à ceux donnés (la non-virgule avant la conjonction), ce va générer des correspondances exactes parfaitement.

Adapter au besoin.

+0

Merci d'avoir fourni 2 réponses détaillées, j'apprécie l'aide. Je viens d'essayer votre premier code, celui qui n'utilise pas regex, et il est en train d'imprimer ceci: '' bacon' 'eggs' fromages obtenus ' 'et' bacon' ' ' fromages obtenus Est considéré comme '' et' 'eggs' fromages obtenus ' 'or' Est considéré comme ' 'bacon' eggs' ' 'or' Est considéré comme Chacun de ces mots sont imprimés sur une nouvelle ligne. Ce que je cherchais, c'est «bacon, fromage et oeufs» sur une ligne comme première allumette, puis «lait, fromage et bacon» sur la ligne suivante, etc. –

+0

(Aussi, intéressant que vous utilisiez les fonctions permutations d'itertools Je l'ai fixé pour mes objectifs à plus long terme pour ce script dans lequel je crée toutes les permutations et les utilise pour remplacer les correspondances.Mais c'est un autre jour!) –

+0

@Darren: Voilà comment je l'ai implémenté, comme je l'ai interprété de votre question originale. La sortie que vous avez citée est les mots qui se trouvent dans chaque ligne à la fois de 'nouns_list' et de' conjonctions'. C'était principalement pour vérifier la sortie. La logique que j'ai utilisée était que si ces mots étaient dans la ligne (numérotée 4), alors c'est une correspondance - ce que vous vouliez, n'est-ce pas? Ou, ai-je malheureusement mal interprété votre question? – jrd1

Questions connexes