2010-06-30 52 views
0

我正在嘗試編寫一些代碼,它可以像谷歌日曆快速添加功能一樣運行。你知道一個你可以輸入以下任何一個的人: 1)2010年9月24日,約翰生日 2)約翰的生日,24/9/10 3)2010年9月24日,約翰的生日 4)24-9 -2010:約翰生日 5)約翰生日2010年9月24日詞法分析或一系列正則表達式將非結構化文本解析爲結構化的表格

而且它可以找出我們想要一個日期24/9/2010的事件有其餘的材料作爲事件文本。

我想這樣做是python。

我正在考慮設計一個可以匹配上面列出的所有情況並提取日期的正則表達式。但我有一個更聰明的方法來解決這個問題。因爲我顯然沒有接受過詞法分析或許多類型的解析器樣式的培訓。我正在尋找什麼是解決這個問題的好方法。

回答

2

注意:這裏的python代碼是不正確的!這只是一個粗略的僞代碼,它看起來如何。

正則表達式擅長以固定格式(例如DD/MM/YYYY日期)從文本中查找和提取數據。

詞法分析器/解析器對擅長處理結構化但有點可變的格式的數據。 Lexers將文本分割爲令牌。這些令牌是給定類型(數字,字符串等)的信息單位。解析器使用這一系列令牌並根據令牌的順序做一些事情。

看一下數據,你有不同的組合基本(主語,動詞,賓語)結構的關係(人,「生日」,日期):

我會處理29/9/10和24-9-2010作爲使用正則表達式的單個令牌,將其作爲日期類型返回。你也許可以做的其他日期相同,與地圖轉換九月九至9

然後,您可以返回至上爲字符串(由空格隔開)。

然後,您有:

  1. 日期 '' '的' 串串
  2. 日期字符串 '生日'
  3. 字符串 '生日' '' 日期
  4. 日期 '生日'「 : '串串 '生日'
  5. 串串 '生日' 日期

注:' birthda Y「」,',‘:’和‘的’在這裏是關鍵字,因此:

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如果是一個元音)。這可能會非常棘手,因爲「亞歷克西斯」可能是「阿列克謝」的複數形式,但因爲你是制約地方複數形式就可以了,很容易發現:

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-name或者是first-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