Как получить результаты от pyparsing Forward object?



Предположим, что у нас есть следующая строка



string = """
object obj1{
attr1 value1;


object obj2 {
attr2 value2;
}
}

object obj3{
attr3 value3;
attr4 value4;
}

"""


Существует вложенный объект, и мы используем Forward для его анализа.



from pyparsing import *
word = Word(alphanums)

attribute = word.setResultsName("name")
value = word.setResultsName("value")

object_grammar = Forward()

attributes = attribute + value + Suppress(";") + LineEnd().suppress()
object_type = Suppress("object ") + word.setResultsName("object_type") + Suppress('{') + LineEnd().suppress()

object_grammar <<= object_type+
OneOrMore(attributes|object_grammar) + Suppress("}") | Suppress("};")

for i, (obj, _, _) in enumerate(object_grammar.scanString(string)):
print('n')
print('Enumerating over object {}'.format(i))
print('n')
print('This is the object type {}'.format(obj.object_type))
print(obj.asXML())
print(obj.asDict())
print(obj.asList())
print(obj)
print(obj.dump())


Вот такие результаты. Параметр obj.функция asXML() содержит всю информацию, однако, поскольку она была сглажена, порядок информации имеет важное значение для анализа результата. Это лучший способ сделать это? Должно быть, я что-то упустил. Я хотел бы получить решение, которое работает как для вложенных, так и для не вложенных объектов, то есть для obj1, obj2 и obj3.

Также, setResultsName('object_type') не возвращает object_type для родительского объекта. Выходные данные программы, описанной выше, показаны ниже. Есть предложения?



Enumerating over object 0

This is the object type obj2

<ITEM>
<object_type>obj1</object_type>
<name>attr1</name>
<value>value1</value>
<object_type>obj2</object_type>
<name>attr2</name>
<value>value2</value>
</ITEM>
{'object_type': 'obj2', 'name': 'attr2', 'value': 'value2'}
['obj1', 'attr1', 'value1', 'obj2', 'attr2', 'value2']
['obj1', 'attr1', 'value1', 'obj2', 'attr2', 'value2']
['obj1', 'attr1', 'value1', 'obj2', 'attr2', 'value2']
- name: attr2
- object_type: obj2
- value: value2


Enumerating over object 1


This is the object type obj3

<ITEM>
<object_type>obj3</object_type>
<name>attr3</name>
<value>value3</value>
<name>attr4</name>
<value>value4</value>
</ITEM>
{'object_type': 'obj3', 'name': 'attr4', 'value': 'value4'}
['obj3', 'attr3', 'value3', 'attr4', 'value4']
['obj3', 'attr3', 'value3', 'attr4', 'value4']
['obj3', 'attr3', 'value3', 'attr4', 'value4']
- name: attr4
- object_type: obj3
- value: value4
503   2  

2 ответов:

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

При определении рекурсивной грамматики обычно требуется сохранить некоторую структуру в выходных результатах. В вашем случае логической частью структуры является содержимое каждого объекта, которое окружено открывающими и закрывающими скобками. Концептуально:
object_content = '{' + ZeroOrMore(attribute_defn | object_defn) + '}'

Тогда опорные выражения справедливы (все еще концептуально):

attribute_defn = identifier + attribute_value + ';'
object_defn = 'object' + identifier + object_content

Фактический Python/pyparsing ибо это выглядит так:

LBRACE,RBRACE,SEMI = map(Suppress, "{};")

word = Word(alphas, alphanums)
attribute = word

# expand to include other values if desired, such as ints, reals, strings, etc.
attribute_value = word 
attributeDefn = Group(word("name") + value("value") + SEMI)

OBJECT = Keyword("object")
object_header = OBJECT + word("object_name")
object_grammar = Forward()
object_body = Group(LBRACE 
                    + ZeroOrMore(object_grammar | attributeDefn)
                    + RBRACE)
object_grammar <<= Group(object_header + object_body("object_content"))

Group делает для нас две вещи: он структурирует результаты в подобъекты; и он удерживает имена результатов на одном уровне от того, чтобы наступать на них на другом уровне (поэтому нет необходимости в listAllMatches).

Теперь вместо scanString Вы можете просто обработать ваш ввод, используя OneOrMore:

print(OneOrMore(object_grammar).parseString(string).dump())

Дача:

[['object', 'obj1', [['attr1', 'value1'], ['object', 'obj2', [['attr2', 'value2']]]]], ['object', 'obj3', [['attr3', 'value3'], ['attr4', 'value4']]]]
[0]:
  ['object', 'obj1', [['attr1', 'value1'], ['object', 'obj2', [['attr2', 'value2']]]]]
  - object_content: [['attr1', 'value1'], ['object', 'obj2', [['attr2', 'value2']]]]
    [0]:
      ['attr1', 'value1']
      - name: attr1
      - value: value1
    [1]:
      ['object', 'obj2', [['attr2', 'value2']]]
      - object_content: [['attr2', 'value2']]
        [0]:
          ['attr2', 'value2']
          - name: attr2
          - value: value2
      - object_name: obj2
  - object_name: obj1
[1]:
  ['object', 'obj3', [['attr3', 'value3'], ['attr4', 'value4']]]
  - object_content: [['attr3', 'value3'], ['attr4', 'value4']]
    [0]:
      ['attr3', 'value3']
      - name: attr3
      - value: value3
    [1]:
      ['attr4', 'value4']
      - name: attr4
      - value: value4
  - object_name: obj3
Я начал просто вносить простые изменения в ваш код, но в вашем оригинале был фатальный изъян. Ваш парсер разделил левую и правую фигурные скобки в два отдельных выражения-в то время как это "работает", это побеждает способность определить групповую структуру результатов.

Я смог обойти это, используя listAllMatches=True в функции setResultsNames. Это дало мне asxml () результат, который имел структуру, из которой я мог извлечь информацию. Он все еще полагается на порядок XML и требует использования zip для получения name и value для attribute вместе. Я оставлю этот вопрос открытым, чтобы посмотреть, есть ли у меня лучший способ сделать это.

Comments

    Ничего не найдено.