【问题标题】:lexical analyse or series of regular expressions to parse unstructured text into structured form词法分析或一系列正则表达式,将非结构化文本解析为结构化形式
【发布时间】:2010-06-30 22:26:43
【问题描述】:

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

它可以计算出我们希望在 2010 年 9 月 24 日发生的事件将其余材料作为事件文本。

我想做的是python。

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

【问题讨论】:

    标签: python regex parsing lexical-analysis


    【解决方案1】:

    注意:这里的python代码不正确!它只是一个粗略的伪代码。

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

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

    查看数据,您有一个基本的(主语、动词、宾语)结构,关系(人、“生日”、日期)的不同组合:

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

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

    然后你有:

    1. 日期','字符串'生日'
    2. 字符串 '生日' ',' 日期
    3. date 'birthday' 'of' 字符串字符串
    4. 日期':'字符串字符串'生日'
    5. 字符串字符串'生日'日期

    注意:'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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-02
      • 1970-01-01
      • 2016-04-18
      • 1970-01-01
      • 1970-01-01
      • 2020-10-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多