从几个列表中的几个元素中查找文本文件中的模式?

时间:2013-09-22 03:20:04

标签: python regex list

我是初学者,作为我的第一个编程语言,已经学习了几个月的python。我希望从文本文件中找到一个模式。我的第一次尝试是使用正则表达式,它确实有效,但有一个限制:

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

所以此时您可能会问为什么这些代码中的列表是因为它们没有被使用。好吧,我一直绞尽脑汁,尝试各种for循环和if函数中的语句,试图找到复制正则表达式模式的原因,但使用列表。

正则表达式的限制是在`noun_list_pattern'中多次找到的\b\w+\w\代码实际上只能找到单词 - 任何单词 - 而不是特定的名词。这可能会引发误报。我想通过使用上面列表中的元素而不是正则表达式来缩小范围。

因为我在正则表达式模式中实际上有4个不同的正则表达式(它包含4个|),所以我将在这里使用其中的1个。所以我需要找到一个模式,如:

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

显然,上面引用的代码行不是真正的python代码,而是我对所需匹配的想法的一种表达。我在哪里说noun in noun_list我的意思是通过noun_list进行迭代; C in CC_list是通过CC_list的迭代; ,是逗号和空格的文字字符串匹配。

希望我已经说清楚了!

以下是我正在使用的test_sentence.txt文件的内容:

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.

2 个答案:

答案 0 :(得分:2)

稍微解决你的问题。首先,您需要一个与列表中的单词匹配的模式,但不需要其他模式。您可以使用交替运算符|和文字词来完成此操作。例如,red|green|blue将匹配"red""green""blue",但不匹配"purple"。使用该字符加入名词列表,并添加单词边界元字符和括号以对替换进行分组:

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

对你的连词列表做同样的事情:

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

您要进行的总体匹配是“一个或多个noun_patt匹配,每个匹配后可选地后跟一个逗号,然后匹配conj_patt,然后再匹配一个noun_patt ”。对于正则表达式来说很容易:

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

您真的不想使用re.findall(),而是使用re.search(),因为您只希望每行匹配一次:

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

作为一个注释,就解析英语而言,如果不是正常表达式的限制,你就会接近。比这更复杂,你可能想看看实际的解析,也许是用NLTK。

答案 1 :(得分:2)

实际上,您不一定需要正则表达式,因为有很多方法可以仅使用原始列表。

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

匹配号为4的原因是4是正确的匹配数。 (注意,对于重复的名词或连词也可能是这种情况)。

编辑:

此版本打印匹配的行和匹配的单词。还修复了可能的多字匹配问题:

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

但是,如果这不适合您,您可以按如下方式构建正则表达式(使用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

这种方法的警告是它是纯粹的暴力,因为它生成两个列表的所有可能的组合(读取排列),然后可以测试它们是否匹配。因此,它非常慢,但在这个例子中匹配给定的(连接前的非逗号),这个将完全生成完全匹配

根据需要进行调整。