词法分析或一系列正则表达式,用于将非结构化文本解析为结构化形式

时间:2010-06-30 22:26:43

标签: python regex parsing lexical-analysis

我正在尝试编写一些功能类似于谷歌日历快速添加功能的代码。您知道可以输入以下任何内容的那个: 1)2010年第24季度,约翰斯生日 2)约翰的生日,10/24/9 3)2010年9月24日,John Doe的生日 4)24-9-2010:约翰生日快乐 5)约翰生日2010年9月24日生日

它可以证明我们希望在24/9/2010日期的事件中将剩余的材料作为事件文本。

我想这样做是python。

我正在考虑一种设计,我可以编写可能与上面列出的所有情况匹配的正则表达式并提取日期。但我觉得有更聪明的方法可以解决这个问题。因为我显然没有接受过词法分析或许多类型的解析器样式的训练。我正在寻找什么是解决这个问题的好方法。

1 个答案:

答案 0 :(得分:2)

注意:这里的python代码不正确!它只是一个粗略的伪代码,可能看起来如何。

正则表达式擅长以固定格式(例如DD / MM / YYYY日期)从文本中查找和提取数据。

词法分析器/解析器对擅长以结构化但有些可变的格式处理数据。 Lexers将文本分成标记。这些令牌是给定类型(数字,字符串等)的信息单位。解析器采用这一系列令牌,并根据令牌的顺序执行某些操作。

查看数据,您可以在关系(人物,'生日',日期)的不同组合中使用基本(主题,动词,对象)结构:

我会使用正则表达式将29/9/10和24-9-2010作为单个标记处理,并将其作为日期类型返回。您可以对其他日期执行相同的操作,使用地图将9月转换为sep至9。

然后您可以将其他所有内容作为字符串返回(由空格分隔)。

然后你有:

  1. date','string'birthday'
  2. 字符串'birthday'','date
  3. 'string string
  4. 的'生日''
  5. date':'string string'birthday'
  6. 字符串字符串'生日'日期
  7. 注意:'birthday',',',':'和'of'这里是关键字,所以:

    class Lexer:
        DATE = 1
        STRING = 2
        COMMA = 3
        COLON = 4
        BIRTHDAY = 5
        OF = 6
    
        keywords = { 'birthday': BIRTHDAY, 'of': OF, ',': COMMA, ':', COLON }
    
        def next_token():
            if have_saved_token:
                have_saved_token = False
                return saved_type, saved_value
            if date_re.match(): return DATE, date
            str = read_word()
            if str in keywords.keys(): return keywords[str], str
            return STRING, str
    
        def keep(type, value):
            have_saved_token = True
            saved_type = type
            saved_value = value
    

    除了3之外的所有人都使用人的占有形式(如果最后一个字符是辅音,则为's,如果是元音,则为s)。这可能很棘手,因为'Alexis'可能是'Alexi'的复数形式,但由于你在限制复数形式的位置,因此很容易被发现:

    def parseNameInPluralForm():
        name = parseName()
        if name.ends_with("'s"): name.remove_from_end("'s")
        elif name.ends_with("s"): name.remove_from_end("s")
        return name
    

    现在,名称可以是first-namefirst-name last-name(是的,我知道日本交换了这些,但从处理角度来看,上述问题不需要区分名字和姓氏)。以下将处理这两种形式:

    def parseName():
        type, firstName = Lexer.next_token()
        if type != Lexer.STRING: raise ParseError()
        type, lastName = Lexer.next_token()
        if type == Lexer.STRING: # first-name last-name
            return firstName + ' ' + lastName
        else:
            Lexer.keep(type, lastName)
            return firstName
    

    最后,您可以使用以下内容处理表单1-5:

    def parseBirthday():
        type, data = Lexer.next_token()
        if type == Lexer.DATE: # 1, 3 & 4
            date = data
            type, data = Lexer.next_token()
            if type == Lexer.COLON or type == Lexer.COMMA: # 1 & 4
                person = parsePersonInPluralForm()
                type, data = Lexer.next_token()
                if type != Lexer.BIRTHDAY: raise ParseError()
            elif type == Lexer.BIRTHDAY: # 3
                type, data = Lexer.next_token()
                if type != Lexer.OF: raise ParseError()
                person = parsePerson()
        elif type == Lexer.STRING: # 2 & 5
            Lexer.keep(type, data)
            person = parsePersonInPluralForm()
            type, data = Lexer.next_token()
            if type != Lexer.BIRTHDAY: raise ParseError()
            type, data = Lexer.next_token()
            if type == Lexer.COMMA: # 2
                type, data = Lexer.next_token()
            if type != Lexer.DATE: raise ParseError()
            date = data
        else:
            raise ParseError()
        return person, date