pyparsing - разобрать xml комментарий

Мне нужно проанализировать файл, содержащий комментарии XML. В частности, это AC# файл с использованием MS /// условность.

Из этого мне нужно вытащить foobar, или же /// foobar было бы приемлемо тоже. (Обратите внимание - это все равно не работает, если вы делаете XML все в одной строке...)

testStr = """
    ///<summary>
    /// foobar
    ///</summary>
    """

Вот что у меня есть:

import pyparsing as pp

_eol = pp.Literal("\n").suppress()
_cPoundOpenXmlComment = Suppress('///<summary>') + pp.SkipTo(_eol)
_cPoundCloseXmlComment = Suppress('///</summary>') + pp.SkipTo(_eol)
_xmlCommentTxt = ~_cPoundCloseXmlComment + pp.SkipTo(_eol)
xmlComment = _cPoundOpenXmlComment + pp.OneOrMore(_xmlCommentTxt) + _cPoundCloseXmlComment

match = xmlComment.scanString(testStr)

и вывести:

for item,start,stop in match:
    for entry in item:
        print(entry)

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

(примечание - я тестировал приведенный выше пример в python 3.2; он работает, но (по моей проблеме) не печатает никаких значений)

Спасибо!

3 ответа

Решение

Как насчет использования nestedExpr:

import pyparsing as pp

text = '''\
///<summary>
/// foobar
///</summary>
blah blah
///<summary> /// bar ///</summary>
///<summary>  ///<summary> /// baz  ///</summary> ///</summary>    
'''

comment=pp.nestedExpr("///<summary>","///</summary>")
for match in comment.searchString(text):
    print(match)
    # [['///', 'foobar']]
    # [['///', 'bar']]
    # [[['///', 'baz']]]

Я думаю Literal('\n') это твоя проблема. Вы не хотите создавать литерал с пробельными символами (поскольку литералы по умолчанию пропускают пробельные символы перед попыткой сопоставления). Попробуйте использовать LineEnd() вместо.

РЕДАКТИРОВАТЬ 1: То, что вы получили бесконечный цикл с LineEnd, не означает, что Literal('\n') лучше. Попробуйте добавить .setDebug() на конце вашего _eol определение, и вы увидите, что оно никогда ничего не соответствует.

Вместо того, чтобы пытаться определить тело вашего комментария как "одну или несколько строк, которые не являются закрывающей строкой, но приводят все к концу строки", что, если вы просто сделаете:

xmlComment = _cPoundOpenXmlComment + pp.SkipTo(_cPoundCloseXmlComment) + _cPoundCloseXmlComment 

(Причина, по которой вы получили бесконечный цикл с LineEnd(), заключалась в том, что вы, по сути, выполняли OneOrMore(SkipTo(LineEnd())), но никогда не использовали LineEnd(), поэтому OneOrMore просто продолжал сопоставлять и сопоставлять и сопоставлять, анализировать и возврат пустой строки, поскольку позиция разбора была в конце строки.)

Вы можете использовать синтаксический анализатор xml для анализа xml. Должно быть легко извлечь соответствующие строки комментариев:

import re
from xml.etree import cElementTree as etree

# extract all /// lines
lines = re.findall(r'^\s*///(.*)', text, re.MULTILINE)

# parse xml
root = etree.fromstring('<root>%s</root>' % ''.join(lines))
print root.findtext('summary')
# -> foobar
Другие вопросы по тегам