python - 如何最好地解析一个简单的语法?

标签 python parsing nlp pyparsing ply

好的,我已经问了一些关于这个项目的小问题,但我仍然对我提出的设计没有太大信心,所以我要问一个更广泛的问题规模。

我正在解析类(class)目录的先决条件描述。描述几乎总是遵循某种形式,这让我觉得我可以解析大部分。

从文本中,我想生成一张类(class)先决条件关系图。 (在我解析数据之后,这部分会很容易。)

一些示例输入和输出:

"CS 2110" => ("CS", 2110) # 0

"CS 2110 and INFO 3300" => [("CS", 2110), ("INFO", 3300)] # 1
"CS 2110, INFO 3300" => [("CS", 2110), ("INFO", 3300)] # 1
"CS 2110, 3300, 3140" => [("CS", 2110), ("CS", 3300), ("CS", 3140)] # 1

"CS 2110 or INFO 3300" => [[("CS", 2110)], [("INFO", 3300)]] # 2

"MATH 2210, 2230, 2310, or 2940" => [[("MATH", 2210), ("MATH", 2230), ("MATH", 2310)], [("MATH", 2940)]] # 3  
  1. 如果整个描述只是一个类(class),直接输出。

  2. 如果类(class)是连体的(“and”),它们都在同一个列表中输出

  3. 如果类(class)不连续(“或”),则它们位于单独的列表中

  4. 这里,我们有“and”和“or”。

一个使它更容易的警告:“and”/“or”短语的嵌套似乎永远不会大于示例 3 中所示的。

最好的方法是什么?我从 PLY 开始,但我不知道如何解决减少/减少冲突。 PLY 的优点是很容易操纵每个解析规则生成的内容:

def p_course(p):
 'course : DEPT_CODE COURSE_NUMBER'
 p[0] = (p[1], int(p[2]))

使用 PyParse,如何修改 parseString() 的输出就不太清楚了。我正在考虑基于@Alex Martelli 的想法,即在对象中保持状态并从中构建输出,但我不确定如何最好地做到这一点。

 def addCourse(self, str, location, tokens):
  self.result.append((tokens[0][0], tokens[0][1]))

 def makeCourseList(self, str, location, tokens):

  dept = tokens[0][0]
  new_tokens = [(dept, tokens[0][1])]
  new_tokens.extend((dept, tok) for tok in tokens[1:])

  self.result.append(new_tokens)

例如,处理“或”的情况:

    def __init__(self):
            self.result = []
            # ...
  self.statement = (course_data + Optional(OR_CONJ + course_data)).setParseAction(self.disjunctionCourses)



 def disjunctionCourses(self, str, location, tokens):
  if len(tokens) == 1:
   return tokens

  print "disjunction tokens: %s" % tokens

disjunctionCourses() 如何知道要分离哪些较小的短语?它得到的只是token,但是目前解析出来的都保存在result中,那么函数怎么知道result中的哪些数据对应了token的哪些元素?我想我可以搜索 token ,然后找到具有相同数据的 result 元素,但这感觉很复杂......

此外,还有许多包含杂项文本的描述,例如:

"CS 2110 or permission of instructor"
"INFO 3140 or equivalent experience"
"PYSCH 2210 and sophomore standing"

但我解析该文本并不重要。

有什么更好的方法来解决这个问题?

最佳答案

def parse(astr):
    astr=astr.replace(',','')
    astr=astr.replace('and','')    
    tokens=astr.split()
    dept=None
    number=None
    result=[]
    option=[]
    for tok in tokens:
        if tok=='or':
            result.append(option)
            option=[]
            continue
        if tok.isalpha():
            dept=tok
            number=None
        else:
            number=int(tok)
        if dept and number:
            option.append((dept,number))
    else:
        if option:
            result.append(option)
    return result

if __name__=='__main__':
    tests=[ ("CS 2110" , [[("CS", 2110)]]),
            ("CS 2110 and INFO 3300" , [[("CS", 2110), ("INFO", 3300)]]),
            ("CS 2110, INFO 3300" , [[("CS", 2110), ("INFO", 3300)]]),
            ("CS 2110, 3300, 3140", [[("CS", 2110), ("CS", 3300), ("CS", 3140)]]),
            ("CS 2110 or INFO 3300", [[("CS", 2110)], [("INFO", 3300)]]),
            ("MATH 2210, 2230, 2310, or 2940", [[("MATH", 2210), ("MATH", 2230), ("MATH", 2310)], [("MATH", 2940)]])]

    for test,answer in tests:
        result=parse(test)
        if result==answer:
            print('GOOD: {0} => {1}'.format(test,answer))
        else:
            print('ERROR: {0} => {1} != {2}'.format(test,result,answer))
            break

产量

GOOD: CS 2110 => [[('CS', 2110)]]
GOOD: CS 2110 and INFO 3300 => [[('CS', 2110), ('INFO', 3300)]]
GOOD: CS 2110, INFO 3300 => [[('CS', 2110), ('INFO', 3300)]]
GOOD: CS 2110, 3300, 3140 => [[('CS', 2110), ('CS', 3300), ('CS', 3140)]]
GOOD: CS 2110 or INFO 3300 => [[('CS', 2110)], [('INFO', 3300)]]
GOOD: MATH 2210, 2230, 2310, or 2940 => [[('MATH', 2210), ('MATH', 2230), ('MATH', 2310)], [('MATH', 2940)]]

关于python - 如何最好地解析一个简单的语法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2945357/

相关文章:

python - 如何创建一个否定矩阵? (Python)

python - 通过 pyodbc 更新 Oracle DB 挂起

PHP - 从特定位置读取 TXT

python - 将单词与字典进行比较并赋值

nlp - 如何从网页访问 Dialogflow V2 API?

python - MongoDB 分组

python - matplotlib, plt.show() 在不同的方法 = 没有 on_clicked

c - 如何解析不能完全放入内存 RAM 中的文件

java - 像时区一样用 "America/New_York"解析日期

ios - 如何从 Swift 中的字符串获取年份部分?