pyparsing — разбор xml-комментария

#python #grammar #xml-comments #pyparsing

#python #грамматика #xml-комментарии #pyparsing

Вопрос:

Мне нужно проанализировать файл, содержащий XML-комментарии. В частности, это файл c #, использующий соглашение 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; он работает, но (по моей проблеме) не выводит никаких значений)

Спасибо!

Ответ №1:

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

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

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

 xmlComment = _cPoundOpenXmlComment   pp.SkipTo(_cPoundCloseXmlComment)   _cPoundCloseXmlComment 
  

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

Комментарии:

1. спасибо за предложение; однако изменение на _eol=pp.LineEnd().suppress() приводит к зависанию / inf-циклу. Не могли бы вы быть немного более конкретными (примечание — вставьте 3 раздела вместе в один файл .py, и код будет выполняться как есть). Спасибо, Майк

2. проголосуйте за объяснение того, что не так. Дух! Я должен был видеть, что я никогда не использовал конец строки 🙂

Ответ №2:

Как насчет использования 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']]]
  

Комментарии:

1. решение @PaulMcGuire тоже будет работать, но это именно то, что я должен использовать (это самое простое …) Спасибо!

Ответ №3:

Для анализа 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
  

Комментарии:

1. Я думал, ты был великолепен в «Бегущем по лезвию».

2. @JFSebastian К сожалению, это не сработало бы в более широкой картине, в которой я столкнулся с этой проблемой. да, я мог бы извлечь все фрагменты xml, как вы предлагаете, но мне нужно также проанализировать исходный код после комментария, и для этого необходим грамматик; выполнение построчного поиска по регулярным выражениям добавит дополнительный цикл по файлу.

3. @mike: регулярное выражение — это всего лишь пример того, как извлекать строки комментариев. В целом вы используете свой анализатор для извлечения соответствующих комментариев (гораздо более простая задача, чем синтаксический анализ xml), и это не мешает вам использовать анализатор xml для анализа xml, если вы сочтете это необходимым.