PyParsing:并非所有令牌都传递给setParseAction()

时间:2010-05-30 22:03:38

标签: python parsing nlp pyparsing

我正在解析像“CS 2110或INFO 3300”这样的句子。我想输出一个格式:

[[("CS" 2110)], [("INFO", 3300)]]

为此,我想我可以使用setParseAction()。但是,print中的statementParse()语句表明实际上只传递了最后一个令牌:

>>> statement.parseString("CS 2110 or INFO 3300")
Match [{Suppress:("or") Re:('[A-Z]{2,}') Re:('[0-9]{4}')}] at loc 7(1,8)
string CS 2110 or INFO 3300
loc: 7 
tokens: ['INFO', 3300]
Matched [{Suppress:("or") Re:('[A-Z]{2,}') Re:('[0-9]{4}')}] -> ['INFO', 3300]
(['CS', 2110, 'INFO', 3300], {'Course': [(2110, 1), (3300, 3)], 'DeptCode': [('CS', 0), ('INFO', 2)]})

我希望所有的令牌都能通过,但它只是['INFO', 3300]。难道我做错了什么?或者还有另一种方法可以产生所需的输出吗?

以下是pyparsing代码:

from pyparsing import *

def statementParse(str, location, tokens):
    print "string %s" % str
    print "loc: %s " % location
    print "tokens: %s" % tokens

DEPT_CODE = Regex(r'[A-Z]{2,}').setResultsName("DeptCode")
COURSE_NUMBER = Regex(r'[0-9]{4}').setResultsName("CourseNumber")

OR_CONJ = Suppress("or")

COURSE_NUMBER.setParseAction(lambda s, l, toks : int(toks[0]))

course = DEPT_CODE + COURSE_NUMBER.setResultsName("Course")

statement = course + Optional(OR_CONJ + course).setParseAction(statementParse).setDebug()

2 个答案:

答案 0 :(得分:2)

如果您在 courseOptional上设置解析操作,那么效果会更好(您只在Optional上设置!):

>>> statement = (course + Optional(OR_CONJ + course)).setParseAction(statementParse).setDebug()
>>> statement.parseString("CS 2110 or INFO 3300")    

给出

Match {Re:('[A-Z]{2,}') Re:('[0-9]{4}') [{Suppress:("or") Re:('[A-Z]{2,}') Re:('[0-9]{4}')}]} at loc 0(1,1)
string CS 2110 or INFO 3300
loc: 0 
tokens: ['CS', 2110, 'INFO', 3300]
Matched {Re:('[A-Z]{2,}') Re:('[0-9]{4}') [{Suppress:("or") Re:('[A-Z]{2,}') Re:('[0-9]{4}')}]} -> ['CS', 2110, 'INFO', 3300]
(['CS', 2110, 'INFO', 3300], {'Course': [(2110, 1), (3300, 3)], 'DeptCode': [('CS', 0), ('INFO', 2)]})

虽然我怀疑你真正想要的是在每个课程上设置解析操作,而不是在语句上设置:

>>> statement = course + Optional(OR_CONJ + course)
>>> statement.parseString("CS 2110 or INFO 3300")                               Match {Re:('[A-Z]{2,}') Re:('[0-9]{4}')} at loc 0(1,1)
string CS 2110 or INFO 3300
loc: 0 
tokens: ['CS', 2110]
Matched {Re:('[A-Z]{2,}') Re:('[0-9]{4}')} -> ['CS', 2110]
Match {Re:('[A-Z]{2,}') Re:('[0-9]{4}')} at loc 10(1,11)
string CS 2110 or INFO 3300
loc: 10 
tokens: ['INFO', 3300]
Matched {Re:('[A-Z]{2,}') Re:('[0-9]{4}')} -> ['INFO', 3300]
(['CS', 2110, 'INFO', 3300], {'Course': [(2110, 1), (3300, 3)], 'DeptCode': [('CS', 0), ('INFO', 2)]})

答案 1 :(得分:2)

为了保持令牌位“CS 2110”和“INFO 3300”,我建议你将你的课程定义包装在一个小组中:

course = Group(DEPT_CODE + COURSE_NUMBER).setResultsName("Course")

看起来你正在解析某种搜索表达式,例如“x和y或z”。这个问题有一些微妙之处,我建议你查看一下pyparsing wiki中关于如何构建这些表达式的一些例子。否则你最终会得到Optional("or" + this)ZeroOrMore( "and" + that)件的鸟巢。作为最后一搏,您甚至可以使用operatorPrecedence的内容,例如:

DEPT_CODE = Regex(r'[A-Z]{2,}').setResultsName("DeptCode")        
COURSE_NUMBER = Regex(r'[0-9]{4}').setResultsName("CourseNumber")
course = Group(DEPT_CODE + COURSE_NUMBER)

courseSearch = operatorPrecedence(course, 
    [
    ("not", 1, opAssoc.RIGHT),
    ("and", 2, opAssoc.LEFT),
    ("or", 2, opAssoc.LEFT),
    ])

(您可能需要从SourceForge SVN下载最新的1.5.3版本才能实现此功能。)