Поиск списков элементов в строке с помощью Pyparsing - PullRequest
1 голос
/ 19 апреля 2020

Я пытаюсь решить следующую проблему с помощью Pyparsing: я хочу найти в строке вхождения трех типов элементов:

  1. строчные слова
  2. строчные слова после буквенная строка "OBJ"
  3. списки, содержащие один из этих элементов, разделенных символом ','

Пример строки может быть "foo bar OBJ baz foo, bar

Я хочу обработать каждый из этих элементов в отдельном действии синтаксического анализа.

Вот мой код:

import pyparsing
from pyparsing import Word, Literal, alphas

def found_word(s, l, t):
    print('word')
def found_obj(s, l, t):
    print('obj')
def found_list(s, l, t):
    print('list')

def process(string):

    word = ~Literal('OBJ ') + Word(alphas.lower())
    word.setParseAction(lambda s,l,t: found_word(s, l, t))
    obj = Literal('OBJ ') +  Word(alphas.lower())
    obj.setParseAction(lambda s,l,t: found_obj(s, l, t))
    item = word | obj
    list = pyparsing.delimitedList(item, delim=',')
    list.setParseAction(lambda s,l,t: found_list(s, l, t))
    element = word | obj | list

    parser = pyparsing.OneOrMore(element)
    parser.searchString(string).pprint()

if __name__ == "__main__":
    process('foo bar OBJ baz foo,bar')

Редактировать: Я поставил некоторые тестовые данные внутри parseActions только для того, чтобы посмотреть, будут ли они вызваны. Желаемый результат будет:

word
word
obj
word
word
list

Фактический вывод:

word
word
obj
word
word

Т.е. parseAction для списка не вызывается Как мне изменить свой код, чтобы добиться этого?

Обновление delimitedList работает не так, как я ожидал. Когда я вызываю

pyparsing.OneOrMore(list).searchString('foo,bar baz')

found_list кажется, вызывается дважды, хотя в моей строке есть только один элемент списка: * 1 034 *

word
word
list
word
list

Ответы [ 2 ]

0 голосов
/ 30 апреля 2020

Причина, по которой ваш list не анализируется, заключается в следующем выражении:

element = word | obj | list

Поскольку вы проверяете word до list (что является действительно ужасным именем переменной при работе в Python, кстати), тогда ведущий "foo" в "foo, bar" обрабатывается как word, так как '|' является энергичным оператором, совпадающим с первым совпадающим выражением.

Это можно исправить, изменив порядок выражений в element:

element = list | word | obj

Или используя '^' вместо '|'. '^' - оператор пациента - он оценивает все альтернативные выражения и выбирает самое длинное совпадение.

element = word ^ obj ^ list

При любом из этих изменений ваш вывод теперь становится:

word
list
word
list
obj
word
word
list

Почему все списки совпадают? Поскольку delimitedList будет соответствовать одному элементу:

>>> wd = Word(alphas)
>>> wdlist = delimitedList(wd)
>>> print(wdlist.parseString('xyz'))
['xyz']

Если вы хотите, чтобы списки имели> 1 элемент, вы можете добавить действие разбора условия:

>>> wdlist.addCondition(lambda t: len(t)>1)
>>> print(wdlist.parseString('xyz')) 
... raises exception ...

Кроме того, delimitedLists не группируют свои результаты автоматически:

>>> print((wd + wdlist).parseString('xyz abc,def'))
['xyz', 'abc', 'def']

Если вы хотите сохранить содержимое списка в виде списка в результатах, оберните выражение списка в группу:

>>> print((wd + Group(wdlist)).parseString('xyz abc,def'))
['xyz', ['abc', 'def']]

Вот моя обновленная версия вашего process() метода:

def process(string):
    print(string)

    word = ~Literal('OBJ') + Word(alphas.lower())
    word.addParseAction(lambda s,l,t: found_word(s, l, t))
    word.setName("word")
    obj = Literal('OBJ') +  Word(alphas.lower())
    obj.setName("obj")
    obj.addParseAction(lambda s,l,t: found_obj(s, l, t))
    item = word | obj
    list = Group(pyparsing.delimitedList(item, delim=',')
                    .addCondition(lambda t: len(t)>1))
    list.setName("list")
    list.addParseAction(lambda s,l,t: found_list(s, l, t))
    element = obj | list | word

    parser = pyparsing.OneOrMore(element)
    parser.searchString(string).pprint()

, который дает такой вывод:

foo bar OBJ baz foo,bar
word
word
word
word
obj
word
word
list
[['foo', 'bar', 'OBJ', 'baz', ['foo', 'bar']]]

Вы заметите, что я добавил setName() вызовов для каждого ваших выражений. Это сделано для того, чтобы я мог добавить setDebug(), чтобы получить отладочный вывод pyparsing. Добавив:

word.setDebug()
obj.setDebug()
list.setDebug()

перед вызовом parseString, вы получите этот результат отладки. Это может помочь объяснить, почему вы получаете реплицированные «слова» в своем примере вывода.

foo bar OBJ baz foo,bar
Match obj at loc 0(1,1)
Exception raised:Expected "OBJ", found 'f'  (at char 0), (line:1, col:1)
Match list at loc 0(1,1)
Match word at loc 0(1,1)
word
Matched word -> ['foo']
Exception raised:failed user-defined condition, found 'f'  (at char 0), (line:1, col:1)
Match word at loc 0(1,1)
word
Matched word -> ['foo']
Match obj at loc 3(1,4)
Exception raised:Expected "OBJ", found 'b'  (at char 4), (line:1, col:5)
Match list at loc 3(1,4)
Match word at loc 4(1,5)
word
Matched word -> ['bar']
Exception raised:failed user-defined condition, found 'b'  (at char 4), (line:1, col:5)
Match word at loc 3(1,4)
word
Matched word -> ['bar']
Match obj at loc 7(1,8)
obj
Matched obj -> ['OBJ', 'baz']
Match obj at loc 15(1,16)
Exception raised:Expected "OBJ", found 'f'  (at char 16), (line:1, col:17)
Match list at loc 15(1,16)
Match word at loc 16(1,17)
word
Matched word -> ['foo']
Match word at loc 20(1,21)
word
Matched word -> ['bar']
list
Matched list -> [['foo', 'bar']]
Match obj at loc 23(1,24)
Exception raised:Expected "OBJ", found end of text  (at char 23), (line:1, col:24)
Match list at loc 23(1,24)
Match word at loc 23(1,24)
Exception raised:Expected W:(abcd...), found end of text  (at char 23), (line:1, col:24)
Match obj at loc 23(1,24)
Exception raised:Expected "OBJ", found end of text  (at char 23), (line:1, col:24)
Exception raised:Expected {word | obj}, found end of text  (at char 23), (line:1, col:24)
Match word at loc 23(1,24)
Exception raised:Expected W:(abcd...), found end of text  (at char 23), (line:1, col:24)
Match obj at loc 23(1,24)
Exception raised:Expected "OBJ", found end of text  (at char 23), (line:1, col:24)
Match list at loc 23(1,24)
Match word at loc 23(1,24)
Exception raised:Expected W:(abcd...), found end of text  (at char 23), (line:1, col:24)
Match obj at loc 23(1,24)
Exception raised:Expected "OBJ", found end of text  (at char 23), (line:1, col:24)
Exception raised:Expected {word | obj}, found end of text  (at char 23), (line:1, col:24)
Match word at loc 23(1,24)
Exception raised:Expected W:(abcd...), found end of text  (at char 23), (line:1, col:24)
[['foo', 'bar', 'OBJ', 'baz', ['foo', 'bar']]]
0 голосов
/ 19 апреля 2020

Попробуйте это:

s = 'foo bar OBJ baz foo,bar'


for w in s.split(' '):
  if w.islower():
    print("word")
  if 'OBJ' in w:
    print("obj")
  if ',' in w:
    print('list')
...