лексический анализ или ряд регулярных выражений для разбора неструктурированного текста в структурированную форму - PullRequest
0 голосов
/ 01 июля 2010

Я пытаюсь написать код, который будет работать как функция быстрого добавления календарей Google.Вы знаете, где можно ввести любое из следующего: 1) 24 сентября 2010 года, день рождения Джона 2) день рождения Джона, 24/9/10 3) 24 сентября 2010 года, день рождения Джона Доу 4) 24-9-2010: ДжонУ Дня Рождения 5) У Дня Рождения 24 сентября 2010 года

И он может выяснить, что мы хотим, чтобы событие в дату 24/9/2010 имело остаток материала в качестве текста события.

Я хочу сделать это на python.

Я имею в виду проект, в котором я пишу регулярные выражения, которые могут соответствовать всем перечисленным выше случаям, и извлекаю дату.Но я уверен, что есть более разумный способ решения этой проблемы.Поскольку я явно не обучен лексическому анализу или многим типам стилей парсеров.Я ищу хороший способ подойти к этой проблеме.

1 Ответ

2 голосов
/ 01 июля 2010

ПРИМЕЧАНИЕ: код Python здесь не правильный!Это всего лишь приблизительный псевдокод того, как он может выглядеть.

Регулярные выражения хороши для поиска и извлечения данных из текста в фиксированном формате (например, дата ДД / ММ / ГГГГ).

Пара лексер / парсер хороша для обработки данных в структурированном, но несколько изменяемом формате.Лексеры разбивают текст на токены.Эти токены являются единицами информации данного типа (число, строка и т. Д.).Парсеры берут эту серию токенов и делают что-то в зависимости от порядка токенов.

Глядя на данные, у вас есть базовая (субъект, глагол, объект) структура в различных комбинациях для отношения (персона, 'birthday ', date):

Я бы обработал 29/9/10 и 24-9-2010 как один токен, используя регулярное выражение, возвращая его как тип даты.Вероятно, вы могли бы сделать то же самое для других дат, используя карту для преобразования сентября и сентября в 9.

Затем вы можете вернуть все остальное в виде строк (разделенных пробелами).

Вызатем имеют:

  1. дата ', строка' день рождения '
  2. строка' день рождения ',' дата
  3. дата 'день рождения' 'строки' строка
  4. date ':' string string 'birthday'
  5. string string 'birthday' date

ПРИМЕЧАНИЕ: 'birthday', ',', ':' and 'из 'здесь есть ключевые слова, поэтому:

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
...